1、委托的类型:
Invoke or BeginInvoke
Invoke或者BeginInvoke方法都需要一个委托对象作为参数。委托类似于回调函数的地址,因此调用者通过这两个方法就可以把需要调用的函数地址封送给界面线程。这些方法里面如果包含了更改控件状态的代码,那么由于最终执行这个方法的是界面线程,从而避免了竞争条件,避免了不可预料的问题。如果其它线程直接操作界面线程所属的控件,那么将会产生竞争条件,造成不可预料的结果。
使用BeginInvoke方法封送一个委托方法,类似于使用PostMessage进行通信,这是一个异步方法。也就是该方法封送完毕后马上返回,不会等待委托方法的执行结束,调用者线程将不会被阻塞。但是调用者也可以使用EndInvoke方法或者其它类似WaitHandle机制等待异步操作的完成。使用了EndInvoke方法则会进行阻塞,其效果跟直接使用Invoke效果一样。
但是在内部实现上,Invoke和BeginInvoke都是用了PostMessage方法,从而避免了SendMessage带来的问题。而Invoke方法的同步阻塞是靠WaitHandle机制来完成的。而BeginInvolve跟EndInvoke一样可以实现同步阻塞。
委托方法很好用,下面就列出一个应用场景,其中就是自定义用户控件,当某个大的用户控件是由小的子用户控件组成时,这时小的用户控件的一些事件则需要通过主控件来响应,这时则可以在子用户控件上定义委托事件,然后委托给主用户控件完成。
例子1.修改价格:
其中一个用户控件的代码如此:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CallBack
{
//把委托事件声明在类外面
//public delegate void EditPriceEvent(object price);
public partial class EditPriceItem : UserControl
{
//声明关于事件的委托,其也可以声明在类外面。
public delegate void EditPriceEvent(object price);
//声明一个事件,其中event可以省略
public EditPriceEvent editpriceevent;
public object NowPrice { get; set; }
public void SetPlateDate(object plate)
{
if(plate!=null)
{
this.NowPrice = plate;
TBPrice.Text = (String)this.NowPrice;
}
}
public EditPriceItem()
{
InitializeComponent();
}
private void EditClick(object sender, EventArgs e)
{
//其判断在别处有没有声明这个事件,这是进行事件委托的格式一
if (this.editpriceevent!=null)
{
float value = int.Parse(this.NowPrice.ToString()) + 2;
this.NowPrice = value;
this.editpriceevent.Invoke(this.NowPrice);
}
//进行事件委托格式二,这个要在高版本的VS上才有,这里是判断事件是否为null,/不为null则进行实际委托,此方法更好,避免了未声明就进行委托报错
//this.editpriceevent?.Invoke(this.NowPrice);
//进行事件委托格式三,这个在IL代码上编译的结果是会自动加上(.Invoke)
//editpriceevent(this.NowPrice);
}
}
}
注意:这里自定义控件尽量不要使用静态变量、静态函数等能够在不同对象上共享的数据的定义。
其中这主界面里的工程代码是:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CallBack
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//添加事件响应函数
editPriceItem1.editpriceevent += new EditPriceItem.EditPriceEvent(changeprice);
}
public void changeprice(object price)
{
//把价格显示到主界面
textBox1.Text =price.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
//把价格发送给小用户控件
editPriceItem1.SetPlateDate(textBox1.Text);
}
}
}
其中自定义的用户控件如图:
其中主界面如下:
2、使用Action进行委托的方法:
其中Action比delegate高级,因为其可以不用事先声明一个委托方法,可以直接声明一个Action对象即可。下面是解释delegate跟Action的方法对比:
1:小明要买一本一个程序员自我修养的书籍(xx书就不买)硬性要求 (这就是要定义委托性质)
代码:
private delegate void BuyBook();
2:附近书店 (委托的方法)
代码:
public static void Book()
{
Console.WriteLine("我是提供书籍的");
}
3:小明和书店建立关系(给委托绑定方法)
代码:
BuyBook buybook = new BuyBook(Book);
4:小明给钱拿书(触发)
buybook();
Action的用法:
- 无参数
1:小明很是苦恼,我就是买一本书籍,每次都让我定义下,烦死了,有没有一种方法不去定义委托呢,那么有吗,还真有,就是我们今天讲的Action
//第一种委托对象声明
Action BookAction = new Action(Book);
//第二种委托对象声明
Action BookAction = Book;
//第一种委托方法执行
BookAction();
//第二种相同的委托方法执行:
//BookAction.Invoke();
注意:此方法是没有参数的委托,其BookAction即会触发Book()函数执行。上面列出了两者声明方法跟执行方法。其效果都是一样的,因为在IL代码解析器上器代码都是一样的。
- 带参数
小明现在又不满意了,我把一个程序员的自我修养看完了,现在呢想买本其他书,那怎么办,我是不是要重新再次定义委托。其实不需要你只需要把参数穿过来就可以了。下面我们看Action<T>的用法
static void Main(string[] args)
{
Action<string> BookAction = new Action<string>(Book);
BookAction("百年孤独");
}
public static void Book(string BookName)
{
Console.WriteLine("我是买书的是:{0}",BookName);
}
现在小明又改变主意了,我不仅要自己选择书籍,我还要在一个牛逼的书籍厂家买,有没有这种方式呢,那么告诉你有,Action<in T1,in T2>
static void Main(string[] args)
{
Action<string,string> BookAction = new Action<string,string>(Book);
BookAction("百年孤独","北京大书店");
}
public static void Book(string BookName,string ChangJia)
{
Console.WriteLine("我是买书的是:{0}来自{1}",BookName,ChangJia);
}
总结
1:Action用于没有返回值的方法(参数可以根据自己情况进行传递)
2:Func恰恰相反用于有返回值的方法(同样参数根据自己情况情况)
3:记住无返回就用action,有返回就用Func
3、Thread声明、锁安全:
- 其中C#中的Thread t = new Thread(new ThreadStart(name))和Thread t = new Thread(name)有区别吗?
这两者是没有区别的,因为MSDN官方是说可以省略ThreadStart或ParameterizedThreadStart来委托构造函数。即在Thread里面函数是委托函数。
- 为什么当使用没有函数的代码时,Thread要使用如下格式:
thread = new Thread(() =>
{
while (true)
{
moveCheck();
}
});
其中【() =>】是一个匿名委托函数,无参数无返回值。其中=>是指向具体指向代码。其中Thread会自动把里面的对象变为委托对象。
//需要加锁的静态全局变量
private static bool _isOK = false;
//lock只能锁定一个引用类型变量
private static object _lock = new object();
static void MLock()
{
//多线程
new System.Threading.Thread(Done).Start();
new System.Threading.Thread(Done).Start();
Console.ReadLine();
}
static void Done()
{
//lock只能锁定一个引用类型变量
lock (_lock)
{
if (!_isOK)
{
Console.WriteLine("OK");
_isOK = true;
}
}
}
注意:这里使用到了lock安全。需要注意的是,Lock只能锁住一个引用类型的对象。另外,除了锁机制外,高版本的C#中加入了async和await方法来保证线程安全,如下所示:
public static class AsynAndAwait
{
//step 1
private static int count = 0;
//用async和await保证多线程下静态变量count安全
public async static void M1()
{
//async and await将多个线程进行串行处理
//等到await之后的语句执行完成后
//才执行本线程的其他语句
//step 2
await Task.Run(new Action(M2));
Console.WriteLine("Current Thread ID is {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
//step 6
count++;
//step 7
Console.WriteLine("M1 Step is {0}", count);
}
public static void M2()
{
Console.WriteLine("Current Thread ID is {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
//step 3
System.Threading.Thread.Sleep(3000);
//step 4
count++;
//step 5
Console.WriteLine("M2 Step is {0}", count);
}
}