20190523
 1、但当你创建了一个很大的类,那么为了方便创建对象,你最好使用var关键字。
 2、类属性需要快速赋值
 tsttab[] mytab = new tsttab[] { new tsttab { ID = 1, name = “”, age = 1 }, new tsttab { ID = 1, name = “”, age = 2 } };
 3、
 三层架构的各层引用添加好以后,架构就搭建完成了。然后就可以在
 实体类库(Model)添加:数据库表和字段;
 数据库访问层(DAL)添加:访问数据库的视图、存储过程的方法。
 业务逻辑层(BLL)添加:DAL层的方法接口;
 最后,表现层(UI)添加:要显示的界面或实现功能;
 4、可空修饰符 datatime?
实现功能:
 1.PLCMC协议
 2.dll连接和使用
 3.多线程编程
 4.重连
 5.串口和网口通讯
 6.数据库三层架构
 7.视觉包调用
 8.webservice调用/http-post
 9.算法编写
 10.写log文件
以上只有第6点涉及架构
20190524
 1.Fileinfo :文件物理地址
 2.Filestream: 文件信息,以文件流的信息,是一条一条遍历
 3.XmlSerializer:序列器,序列化和反序列化
2020年6月28日11:42:24
 1.internal修饰符,指定该对象只能在程序集中访问。
 程序集:dll或者exe
 1楼的bai答案正确我来补充一下,对于一些大型的项目,du通常由很多个DLL文件zhi组成,引用dao了这些DLL,就能访问DLL里面的类和类里面的方法。比如,你写了一个记录日志的DLL,任何项目只要引用此DLL就能实现记录日志的功能,这个DLL文件的程序就是一个程序集。如果你记录日志的程序集是这么定义的namespace LogerHelper { internal class aa { public void bb() { return “”; } } public class Write { public void WriteIn(string content) { class x = new aa(); x.bb(); } }}当另一个项目引用了此DLL它可以这么访问 LogerHelper.Write x = new LogerHelper.Write();x.WriteIn(“”);但不可以这么访问LogerHelper.aa x = new LogerHelper.aa();x.bb();这就叫,只能在程序集中访问
在这个属性之上有两个Attribute,这两个attribute描述了控件在设计时所表现出来的特征。我们来看看在控件设计中有哪些主要用到的设计时Attribute。
 BrowsableAttribute:描述是否一个属性或事件应该被显示在属性浏览器里。
 CategoryAttribute:描述一个属性或事件的类别,当使用类别的时候,属性浏览器按类别将属性分组。
 DescriptionAttribute:当用户在属性浏览器里选择属性的时候,description里指定的文本会显示在属性浏览器的下边,向用户显示属性的功能。
 BindableAttribute:描述是否一个属性倾向于被绑定。
 DefaultPropertyAttribute:为组件指定一个默认的属性,当用户在Form设计器上选择一个控件的时候,默认属性会在属性浏览器里被选中。
 DefaultValueAttribute:为一个简单类型的属性设置一个默认值。
 EditorAttribute:为属性指定一个特殊的编辑器。
 LocalizableAttribute:指示一个属性是否能被本地化,任何有这个Attribute的属性将会被持久化到资源文件里。
 DesignerSerializationVisibilityAttribute:指示一个属性是否或者如何持久化到代码里。
 TypeConverterAttribute:为属性指定一个类型转换器,类型转换器能将属性的值转化成其它的数据类型。
 DefaultEventAttribute:为组件指定一个默认的事件,当用户在form设计其中选择一个控件的时候,在属性浏览器中这个事件被选中。
2020年6月30日14:56:49
 1.控制流指的是程序从头到尾的执行流程
 2.引用参数:必须在方法的声明和调用中都是用ref修饰符
 3.输出参数:必须在方法的声明和调用中都是用out修饰符
 1)在方法内部,必须对输出参数进行一次赋值
 2)可以不在调用方法之前赋值
 4.参数数组:在数据类型前是用params修饰符
 1)在声明中需要修饰符
 2)在调用中不允许修饰符
 3)用于传递可变数目的实参到方法
 5.在调用中的实参可以用变量名+:来指定变量
 6.可选参数:给定默认值,再调用的时候可不用重新赋值
 7.
 [特性][修饰符] 核心声明
 1)多个特性或者多个修饰符,可以任意顺序
 2)public static <-> static public
 8.一个实例一个副本
 9.即使类没有实例,也存在静态成员,并且可以访问
 10.静态函数同理,没有实例也能调用,但是静态函数成员不能访问实例成员,
 可以访问其他静态成员。
 静态成员不是实例的一部分,所以不能在任何静态函数成员的代码中使用this关键字
 11.属性:
 1)是一个函数成员
 2)它执行代码
 3)通过访问器set/get访问
 4)读写会隐式调用set/get方法
 5)不分配内存
 12.readonly修饰符:
 1)类似const
 2)只可以在构造函数中初始化
 13.索引器
 string this [int index]
 {
 set{SetAccessorCode}
 get{GetAccessorCode}
 }
 14.
 分部类:可以用修饰符修饰部分类的实现
 partial class MyPartClass
 {
 public void Output1(int inval)
 {
 }
 }
 partial class MyPartClass
 {
 public void Output2(int inval)
 {
 }
 }
 15.类智能直接继承一个基类
 16.派生类
 在修饰符前面加new可以屏蔽掉基类的同样签名变量
 17.可以通过base来访问基类的成员
 18.基类 base1 = (基类)派生类->base1可以访问基类的成员
 19.在构造函数中调用虚方法是极不推荐的
 19.派生类在实例化的时候,会先调用基类的构造函数,再调用自身的构造函数,会默认调用无参数的构造函数,如果需要调用其他的构造函数,需重新指定->:base(VAR)
 或者:this()
 20.标记为public的类可以被系统内任何程序集中的代码访问。
 标记为internal的类只能被它自己所在的程序集内的类看到。(默认的可访问级别)
 21.abstact修饰符:必须被派生类覆写,并且没有实现体,被分号取代。
 virtual不必须被派生类覆写,有实现体
 abstact和virtual都需要用override来重写
 22.抽象类:
 1)抽象类只能被用作其他类的基类
 2)其派生类需要负责实现抽象类中所有抽象成员,如果其派生类也未抽象类则不用
 3)抽象类不能实例化
 23.密封类:
 1)不能被继承,用sealed修饰
 24.静态类中所有成员都是静态的。静态类用于存放不受实例影响的数据和函数。静态类的一个常见的用途是创建一个包含一组数学方法和值的数学库
 25.静态类:
 1)类的所有成员必须是静态的。
 2)类可以有一个静态构造函数,但是不能有实例构造函数,不能创建该类的实例
 3)隐式密封
 26.
 方式一:
 static class ExtendMyData
 {
 public static double Average(MyData md)
 {
 }
 }
 -> ExtendMyData.Average(md)
 方式二:
 static class ExtendMyData
 {
 public static double Average(this MyData md)
 {
 }
 }
 -> md.Average()
 1)方法和类都是静态
 2)拓展方法包含关键字this作为它的第一个参数类型
 27.
 类型转换:
 方法必须是静态的,公共的
 隐式转换:
 class LimitedInt
 {
 public static implicit operator int(LimitedInt li)
 {
 return li.TheValue
 }
 } ->LimitedInt li = 500
 显式转换:
 class LimitedInt
 {
 public static explicit operator int(LimitedInt li)
 {
 return li.TheValue
 }
 }->LimitedInt li = (LimitedInt) li
 28.运算符重载
 public static LimitedInt operator +(LimitedInt x,double y)
 29.
 using System.Reflection;
 Type t = typeof(SomeClass)
 FieldInfo[] fi = t.GetFields() ->获取字段
 Method[] mi = t.GetMethods() ->获取方法
 30.资源是指一个实现了System.IDisposeable接口的类或者结构。
 31.
 结构:
 1)类是引用类型,而结构是值类型
 2)结构可以有实例构造函数和静态构造函数,不允许有析构函数,结构也可以实例化
 3)当不使用new来创建实例的时候。会有两个限制①显式摄之了数据成员之后,才能使用他们的值;②对所有数据成员赋值之后,才能试调用任何函数成员
 🔺静态构造函数可以创建并初始化静态数据成员,但是不能引用实例成员
32.Flags特性不会改变计算结果,但却提供了一些方便的特性。首先,它通知编译器,对象浏览器以及其他查看这段代码的工具,该枚举的成员不仅可以用作单独的值,还可以按位标志进行组合。这样浏览器就可以更恰当地解释该枚举类型的变量
 33.枚举变量,将其继承uint后,用16进制来表示,可以用HasFlag来判度哪个位标志为真
 enum CardDeckSetting:uint
 {
 SingleDeck = 0x01,
 LargePicture = 0x02,
 FancyNumbers = 0x04,
 Animation = 0x08
 }
 34.
 枚举:声明的成员变量
 1)不能对成员使用修饰符,他们都隐式地具有和枚举相同的可访问性
 Enum.GetName(typeof(trafficlight),1)
 Enum.GetNames(typeof(trafficligtht))
 35.委托:
 1)声明一个委托类型 delegate void Mydel(int value)
 2)声明一个委托变量 Mydel del = new Mydel(); ->del += func
 3)实例化委托对象 del = randomValue < 50?new Mydel()
 4)调用del(1)会间接调用func(1)
 匿名委托
 Otherdel del = delegate(int x){};
2020年7月1日08:54:06
 1.事件:
 事件提供了对它的私有控制委托的结构化访问,外部无法直接访问委托
 事件中可用的操作比委托少,只能增删调用
 事件被触发时,调用委托来依次调用调用列表中的方法
 2.
 事件声明:public event EventHandler CountedDozen
 静态事件声明:public static event EventHandler CountedDozen
 △事件是成员,是类或者结构的成员。
 订阅事件:
 incrementer.CountedDozen += IncrementDozensCount;
 incrementer.CountedDozen +=()=>DonzensCount++;
 incrementer.CountedDozen +=delegate{DonzensCount++;}
 触发事件:
 if(CountedDozen!=null)
 CountDozen(source,args);
 3.标准事件用法:
 public delegate void EventHandler(object sender,EventArgs e);
 1)保存触发事件的对象的引用,可以匹配任何类型的实例
 2)保存参数信息,需要声明一个派生自EventArgs的类
 自定义参数事件:
 public event EventHandler OnProperChanged;
 4.接口:
 interface IInfo =>仅提供方法签名,不实现
 {
 string GetName();
 string GetAge();
 }
 class CA:IInfo =>继承接口的类负责实现
 {
 public string GetName(){}
 public string GetAge(){}
 }
 static void PrintInfo(IInfo item){} =>接口可以做形参
 CA a = new CA();
 PrintInfo(a); =>只要继承了这个接口的类,都能作为实参传递进去
 △声明一个类继承于ICompare,然后重写compareto,就能使用Array.Sort(ICompare Item)
 △接口是隐式public
 △继承接口的类需要为每一个成员函数进行实现,和abstact类中的方法一样
 △类继承接口的时候,基类必须写在最前
 △接口是引用类型 怎样理解?
 △类可以用括号内接口来强制转换成接口,然后调用接口的方法,也可以用as
 △如果继承多个接口,而个别接口需要实现的方法重名,可以用冒号指定实现哪个,或者直接只写一个,如果该类为派生类,而基类已经实现了同名方法,则该类可以不写同名方法。另外同名方法,可以强制转换类来访问
 △接口可以继承接口
 5.转换
 check和uncheck可以检测溢出,抛出Overflowexception
 类能隐式转换到它继承链的任何类
 装箱是创建副本,改变箱内的值,不会改变别的值
 public static implicit/explicit operator int(person p)
 可以用is运算符来判断是否能转换为目标类型,不能用于用户自定义转换
 可以用as来强制转换为目标类型,不能用于用户自定义转换
 6.泛型
 泛型类:
 SomeCls<T1,T2>
 T1,T2称为类型参数
 由于T1,T2不是接受所有类型,可以增加约束,用where
 SomeCls<T1,T2>
 where S:IComparable
 泛型方法
 public void PrintData<S,T>(S p,T t)where S:Person{}
2020年7月1日16:04:43
 .net控件开发:
 1.控件是具有用户界面的可视化的组件,例如文本框,列表框
 2.
 在实际生产中,一般都是将自定义的控件编译为一个 dll 来使用,也就是放在一个类库
 中。由于窗体控件开发需要引入一些标准类库外的窗体控件相关的库(System.Drawing
 和 System.Windows.Forms 等),为了减少导入库的操作,可以新建一个窗体应用程序,
 然后将项目属性中的输出类型设置为类库即可
 3.
 public delegate void GetHeartDataDelegate(HeartModel model); //定义一个委托
 public GetHeartDataDelegate GetHeartHandler;
 //MQTT接收数据处理
 void MQTTReceiver(object sender, MqttMsgPublishEventArgs e)
 {
 string receiveData = Encoding.Default.GetString(e.Message);
 if(null != GetHeartHandler)
 {
 //省略转换
 GetHeartHandler(receiverData); //调用委托函数
 }
 }
//外部调用
 void GetHeartDataCallback(HeartModel model)
 {
 //省略实现代码
 }
 void Main()
 { GetHeartHandler=new GetHeartDataDelegate (GetHeartDataCallback); //绑定需要回调的函数 }
4.控件:
 [Browsable(true)] //属性浏览器可以看到这个属性
 [Category(“LabelText”)] //属性浏览器中该控件的分组
 [Description(“标签的文本”)] //属性浏览器中该控件的描述
 public string LabelText //属性
 {
 get {return this.labelInfo.Text;}
 set {this.labelInfo.Text = value;}
 }
 5.//在属性面板中进行隐藏,并且重写后只读,不允许通过该属性进行设置 [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
2020年7月8日10:45:29
 1.先执行Form_load,然后才执行Onpaint
这种在 Click 事件中绘图的方法和在 OnPaint 事件中绘图的方法有着较大的区别,当你将窗体最小化或者隐藏(最大化或者窗体大小调整不影响)再最大化或者显示时,第二个方块就自动消失。对于这种很是奇怪的现象,对于刚接触 GDI+的人来说,确实有点摸不着头脑,究其原因是当最小化或者隐藏窗体时,操作系统将窗体进行了重绘,也就是再次调用了 Paint 事件,原有的绘图都被重新抹掉。
接着上面的话题,当窗体最小化或者隐藏时,操作系统就会将窗体的 UI 资源在内存中释放掉,然后当窗体恢复时,操作系统会发送消息让窗体调用 Control.OnPaint()方法来
 重新创建窗体 UI。
 这种架构之所以这样处理,是由于以前的计算机资源不像现在这样比较“富裕”,图形资源占用大量的内存等资源,如果不及时释放掉,会严重消耗掉内存。
 如果有一个窗体被另外一个窗体遮挡住一部分,那么被遮挡区域的控件将被重绘。
 因此,基于历史遗留的架构设计,对于窗体或者控件的绘制逻辑建议放在 Paint 事件中,放在其他地方往往不能按照预期进行绘制。那么又一个问题来了,如果我们就是需要在其他方法中调用绘图逻辑,那该怎么办呢?其实解决方法是将绘图逻辑依然放在 Paint 事件,其他方法可以配置相关属性,然后通过 Invalidate()来调用 Paint 事件进行绘图即可。
4.//初始化画笔,初始化画板,绘制,释放资源
5.我们在处理绘制逻辑时,不应该在其他方法中直接调用 Paint事件处理程序或者 OnPaint() 方法。特别是绘图逻辑非常复杂的情况,往往会导致性能的降低和不期望的结果出现。对于这种需求,推荐使用 Invalidate()方法。Invalidate()方法可以重绘之前,可以保存相关变量的信息,如果 Invalidate()方法多次,往往会进行合理排队,按期望的顺序执行。Invalidate()方法如果连续执行多次,往往只需要 Paint事件一次,但是如果直接调用 Paint 事件事件,那么调用几次就是重绘几次,因此这两种处理方法的性能存在不小的差距。 推荐使用Invalidate方法来进行重绘。
6.这个问题出现的原因是.NET 框架中的窗体假设只需要绘制隐藏或者恢复的区域,其他区域无需绘制,但是在窗体大小变化的情况下,基于这种假设就是不正确的。
2020年7月13日15:40:55
- StringFormat CenterSF = new StringFormat
 {
 Alignment = StringAlignment.Center,
 LineAlignment = StringAlignment.Center
 };
 2.在 GDI+绘图过程中,如果不做双缓冲(Double Buffering)处理,那么可能会发现
 窗体在大小调整等操作导致界面重绘的时候,界面会闪烁,为了解决这个闪烁的问题,建议
 将控件的双缓冲机制打开:
 //不重绘控件背景 this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); //控件开启双缓冲 this.DoubleBuffered = true;
Bitmap bitmap = new Bitmap(this.ClientRectangle.Width,this.ClientRectangle.Height);
 Graphics g = Graphics.FromImage(bitmap);
 //将内存中创建完成的图形拷贝到界面上
 e.Graphics.DrawImageUnscaled(bitmap,0,0);
 g.Dispose();
 bitmap.Dispose();
4.局部刷新
 Invalidate(rec)可以让界面只刷新对于区域的内容,而
 Invalidate()则刷新整个界面。对于上面的代码,再测试过程中,当有90个级以上的矩形
 时,再次单击界面区域添加矩形时,可以发现有较为明显的闪烁,但是用Invalidate(rec)
 方法来进行局部刷新却没有见闪烁。
5.获取窗体画板
 1)OnPaint(PaintEventArgs e)->protected override void OnPaint(PaintEventArgs e)
 e.Graphics
 2)获取控件画板
 Graphics g = new this.groudbox1.CreateGraphics();
Invalidate(rec)可以让界面只刷新对于区域的内容,
 而Invalidate()则刷新整个界面。
显示图像:
 Bitmap bitmap = new Bitmap(“C:\Users\mm\Desktop\App2020年7月13日122213\App\Resources\ExternalFunc.png”);
 Graphics g = Graphics.FromImage(bitmap);
 e.Graphics.DrawImageUnscaled(bitmap, 0, 0);
 bitmap.Dispose();
 显示背景线
 Pen p = new Pen(Color.FromArgb(0, 255, 0), 1);
 int hLineNums;
 int wLineNums;
 hLineNums = this.Height / gap;
 wLineNums = this.Width / gap;
 //横线
 for (int i = 0; i < hLineNums; i++)
 {
 e.Graphics.DrawLine(p, 0, i * gap, this.Width, i * gap);
 }
 //纵线
 for (int j = 0; j < wLineNums; j++)
 {
 e.Graphics.DrawLine(p, j * gap, 0, j * gap, this.Height);
 }
8.,当窗体最小化或者隐藏时,操作系统就会将窗体的 UI 资源在内存中
 释放掉,然后当窗体恢复时,操作系统会发送消息让窗体调用 Control.OnPaint()方法来
 重新创建窗体 UI
 其实解决方法是将绘图逻辑依然放在 Paint 事件,其
 他方法可以配置相关属性,然后通过 Invalidate()来调用 Paint 事件进行绘图即可
 Invalidate()会优化次数
 Refresh()刷新,重绘界面,不会被优化次数
9.扩展控件即继承自现有的基本控件,通过创建属性和方法来扩展原有
 控件的功能。
2020年8月4日14:42:03
 1.
 public partial class Form1 : Form
 {
 public Form1()
 {
 InitializeComponent();
 }
 public delegate void mydel(int i);
 public EventHandler myEvent;
 private void Form1_Load(object sender, EventArgs e)
 {
 mydel delinstance = add;
 delinstance(1);
 myEvent += EventPaste;
 myEvent(sender, e);
 }
 public void EventPaste(object sender, EventArgs e)
 {
 MessageBox.Show(“m”);
 }
 public void add(int i)
 {
 MessageBox.Show(“n”);
 }
 }
 2.
 用户控件(User Control):继承自 UserControl,主要用于开发 Container 控件,Container控件可以添加其他Controls控件。
 自定义控件(Custom Control):继承自 Control,主要用于开发windows控件的最基本的类,比如 Text,Button 控件。
用户控件是有容器特性的
 自定义控件多用于拓展控件,继承现有控件
 3.
 在GDI+绘图过程中,如果不做双缓存处理,那么可能会发现窗体在大小调整等操作导致界面重绘的时候,界面会闪烁,为了解决这个闪烁的问题,建议将控件的双缓冲机制打开
 //不重绘控件背景
 this.SetStyle(ControlStyles.AllPaintingInWmPaint,true);
 //控件开启双缓冲
 this.DoubleBuffered = true;
 另外,自定义控件的时候,也可以创建双缓冲控件,对于开启双缓冲的控件来说,可以在内存中创建图形,然后将其创建好的图形一次性绘制到界面上,从而减少闪烁。下面的代码将大应用于自定义绘制的控件开发中:
 Bitmap bitmap = new Bitmap(this.ClientRectangle.Width,this.ClientRectangle.Height);
 Graphics g = Graphics.FromImage(bitmap)
 //将内存中创建完成的图形拷贝到界面上
 e.Graphics.DrawImageUnscaled(bitmap,0,0);
 g.Dispose();
 bitmap.Dispose();
private void Show(Control control)
 {
 // 创建缓冲图形上下文
 BufferedGraphicsContext bct = new BufferedGraphicsContext();
 // 创建指定大小缓冲区
 BufferedGraphics bg = bct.Allocate(control.CreateGraphics(), new Rectangle(new Point
 (0, 0), control.Size));
 //创建GDI+画图区域
 Graphics g = bg.Graphics;
 //清除背景颜色(变为默认背景颜色)
 g.Clear(this.BackColor);
 //跟平常Graphics绘图一样
 Pen p = new Pen(Color.Red, 1);
 g.DrawRectangle(p, 5, 5, 50, 50);
 // 将双缓冲区中的图形渲染到指定画布上
 bg.Render(control.CreateGraphics());
 //释放
 bg.Dispose();
 g.Dispose();
 }
 要放在OnPaint当中
 protected override void OnPaint(PaintEventArgs e)
ImageScalingSize修改toolstrip大小,
2020年8月6日10:42:21
 1.
 画图
 Bitmap img = new Bitmap(“C:\Users\mm\Desktop\company.jpg”);
 g.InterpolationMode = System.Drawing.Drawing.InterpolationMode.HighQualityBicubic;
 g.DrawImage(img,new Rectangle(1,1,panel1.Width,panel1.Height));
 2.
 双缓冲图形
 BufferedGraphicsContext bct = new BufferedGraphicContext();
 BufferedGraphics bg = bct.Allocate(panel1.CreateGraphic(),new Rectangle(new Point(0,0),panel1.size))
 3.
 SuspendLayout方法作何用?
 在添加或移除子控件,控件的边界改变,以及在发生其他可影响控件布局的变化时,会发生 Layout 事件。可以使用SuspendLayout挂起布局,可以在控件上执行多个操作,而无需为每次更改执行一次布局操作。也就是说,有了这个语句之后,紧接着下面的添加删除子控件,或者改变子控件的大小、位置及改变它自身的位置的这些操作,都不在引发Layout事件了。
 4.
 工具栏新增
 Bitmap img = new Bitmap(“C:\Users\mm\Desktop\company.jpg”);
 ToolStripButton TSB1 = new ToolStripButton(“TSB1”, img);
 TSB1.Size = new Size(32, 32);
 ToolStripButton TSB2 = new ToolStripButton(“TSB2”, img);
 TSB2.Size = new Size(32, 32);
 toolStrip1.Items.AddRange(new ToolStripButton[] { TSB1, TSB2 });
 5.
 新建按钮
 Button myb = new Button();
 Bitmap myBM = new Bitmap(“C:\Users\mm\Desktop\company.jpg”)
 myb.BackgroundImage = myBM;
 myb.Location = new Point(100,100);
 myb.Size = new Size(75,75)
 myb.Visible = true;
 myb.Name = “Name”;
 myb.Text = “Text”;
 myb.Visible = true;
 this.Controls.Add(myb);
//ele的元素中包含Control,Control Cotrol
 ele.Control.MouseClick += new MouseEventHandler(this.Elementclick);
 ele.Control.MouseDoubleClick += new MouseEventHandler(this.ElementDoubleClick);
 ele.Control.MouseMove += new MouseEventHandler(this.ElementMouseMove);
 ele.Control.MouseDown += new MouseEventHandler(this.ElementMouseDown);
 ele.Control.MouseUp += new MouseEventHandler(this.ElementMouseUp);
//ele设置菜单
 ele.Cotrol.ContextMenuStrip = new ContextMenuStrip
 { new items
 {“修改名称”,null,new EventHandler(this.OnMenuClick)},
 {“设置关系”,Resources.documentmap_16x16,new EventHandler(this.OnMenuClick)
 },}
internal class PanelWithoutAutoScroll : Panel
private static object obj = new object();
 lock(obj)
 {}
 假设线程A先执行,线程B稍微慢一点。线程A执行到lock语句,判断obj是否已申请了互斥锁,判断依据是逐个与已存在的锁进行object.ReferenceEquals比较(此处未加证实),如果不存在,则申请一个新的互斥锁,这时线程A进入lock里面了。
 这时假设线程B启动了,而线程A还未执行完lock里面的代码。线程B执行到lock语句,检查到obj已经申请了互斥锁,于是等待;直到线程A执行完毕,释放互斥锁,线程B才能申请新的互斥锁并执行lock里面的代码。
可以不在OnPaint中重绘
9.控件添加重绘事件
 this.paint.Paint += new PaintEventHandler(this.Onpaint);
 private void Onpaint(object sender, EventArgs e)
 {//TODO}
 其中this.Onpaint要满足object sender,EventArgs e的签名
判断是否为空之后,再执行操作
创建一个Element
 1)设置图片
 2)设置Tag 和 name
 3)设定触发事件
 ele.Control.MouseClick += new MouseEventHandler(this.Elementclick);
 ele.Control.MouseDoubleClick += new MouseEventHandler(this.ElementDoubleClick);
 ele.Control.MouseMove += new MouseEventHandler(this.ElementMouseMove);
 ele.Control.MouseDown += new MouseEventHandler(this.ElementMouseDown);
 ele.Control.MouseUp += new MouseEventHandler(this.ElementMouseUp);
用户控件是有容器特性的
 自定义控件多用于拓展控件,继承现有控件
把AutoSize设为false,就能手动指定Width和Height了!
14.调用WINAPI
 using
BackImage.Images.Find((ImageResoure o) => o.Imagekey == imageName).index;
 }
1)Action封装的方法没有参数也没有返回值,声明原型为:
 public delegate void Action();
 2)Action是Action的泛型实现,也是没有返回值,但可以传入最多16个参数,两个参数的声明原型为:
 public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
 3)Func委托始终都会有返回值,返回值的类型是参数中最后一个,可以传入一个参数,也可以最多传入16个参数,但可以传入最多16个参数,两个参数一个返回值的声明原型为:
 public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
 4)Predicate委托表示定义一组条件并确定指定对象是否符合这些条件的方法,返回值始终为bool类型,声明原型为:
 public delegate bool Predicate(T obj);
 △:
 如果要委托的方法没有参数也没有返回值就想到Action
 有参数但没有返回值就想到Action
 无参数有返回值、有参数且有返回值就想到Func
 有bool类型的返回值,多用在比较器的方法,要委托这个方法就想到用Predicate
2020年8月20日12:09:18
 1.TOOL和MAP之间怎样交互信息的
2.简单的说,如果有两个线程,Thread A和Thread B,并且有一个Control c,是在Thread A里面new的。
那么在Thread A里面运行的任何方法调用c.InvokeRequired都会返回false。
相反,如果在Thread B里面运行的任何方法调用c.InvokeRequired都会返回true。
是否是UI线程与结果无关。(通常Control所在的线程是UI线程,但是可以有例外)
private delegate void InvokeCallback( string msg);
然后就是判断这个属性的值来决定是否要调用Invoke函数:
void m_comm_MessageEvent( string msg)
{
 if (txtMessage.InvokeRequired)
 {
 InvokeCallbackmsgCallback  =   new  InvokeCallback(m_comm_MessageEvent);
 txtMessage.Invoke(msgCallback,  new   object []  { msg } );
} 
 else 
 {
 txtMessage.Text  =  msg;
} 
}
2020年10月14日09:06:56
 1.
 EventHandler就是系统预设的事件签名
 public delegate void EventHandler(object sender, EventArgs e);
 声明事件之后,用+注册事件
 _tool.ToolMouseDown += new EventHandler(ToolWasMouseDown);
 就可以注册一个事件
 2.
 MenuStrip:为系统提供菜单
 ToolStrip:工具
 ToolStripItem toolstripItem = _menuStrip.Items.Add(name[i], images[i], new EventHandler(OnToolButtonClick));
3.lambda:匿名函数
 Element selectElement = ToolElements.Find((Element o) => o.Name == item.Name);
Location:该控件左上角相对于容器左上角的坐标
鼠标点击,带控件移动:
 //让控件跟随鼠标移动,这个e是相对控件容器左上角的坐标
 private void Map_MouseMove(object sender, MouseEventArgs e)
 {
 if (this.panelPlace != null)
 {
 this.panelPlace.Location = new Point(e.X, e.Y);
 _location.X = e.X;
 _location.Y = e.Y;
 }
 }
 //再次点击之后,可以通过赋值Location来达到定点放置效果
 Func.Location = this.panelPlace.Location;
控件自身被长按,控件自身拖曳移动:
private void Mouse_Move(object sender, MouseEventArgs e)
 {
 if (e.Button == MouseButtons.Left)
 {
 //Control.MousePosition是屏幕坐标,鼠标光标位置
 Point mousePos = Control.MousePosition;
            mousePos.Offset(mouse_offset.X, mouse_offset.Y);
            Point location = ((Control)sender).Parent.PointToClient(mousePos);
            ((Control)sender).Location = ((Control)sender).Parent.PointToClient(mousePos);
            this._xcol = ((Control)sender).Parent.PointToClient(mousePos).X;
            this._yrow = ((Control)sender).Parent.PointToClient(mousePos).Y;
        }
}
//Control.MousePosition是屏幕坐标,鼠标光标位置,屏幕左上角的坐标为(0,0)
 Point mousePos = Control.MousePosition;
 //就单纯地偏移,是坐标相加
 mousePos.Offset(mouse_offset.X, mouse_offset.Y);
//屏幕坐标转换为工作区坐标
 Point mousePos = Control.MousePosition;
 Point location = this.PointToClient(mousePos);
private void Mouse_Move(object sender, MouseEventArgs e)
 {
 if (e.Button == MouseButtons.Left)
 {
 //Control.MousePosition是屏幕坐标,鼠标光标位置
 Point mousePos = Control.MousePosition;
 mousePos.Offset(mouse_offset.X, mouse_offset.Y);
 Point location = ((Control)sender).Parent.PointToClient(mousePos);
            //修改当前控件location
            //1.获取鼠标在当前屏幕的坐标A
            //2.以A坐标为参数输入到当前控件的父面板,获取鼠标在父面板的坐标B
            //3.将该坐标赋值到当前控件的Location
            ((Control)sender).Location = ((Control)sender).Parent.PointToClient(mousePos);
            this._xcol = ((Control)sender).Parent.PointToClient(mousePos).X;
            this._yrow = ((Control)sender).Parent.PointToClient(mousePos).Y;
        }
    }
sender.ToString就是点击的字符串,来选择对应的分支
 private void OnMenuClick(object sender, EventArgs e)
 {
 string text = sender.ToString();
 switch (text)
 {
 case “修改名称”:
 MessageBox.Show(_tag);
 break;
 case “设置关系”:
 MessageBox.Show(_name);
 break;
 case “删除”:
 if(OnDeleteClick != null)
 OnDeleteClick(SelectElement);
 break;
 default:
 break;
 }
 }
 9.
 枚举器
 using (List.Enumerator enumerator = ships[i].Fathers.GetEnumerator())
 {
 while (enumerator.MoveNext())
 {
 Element item = enumerator.Current;
 int item2 = ships.FindIndex((RelationShip o) => o.Element == item);
 serial.father.Add(item2);
 }
 }
9.问题在于怎样实现分组










