无法将匿名方法转换为System.Delegate

在WinForm中,不允许非UI线程访问UI,如果非UI线程需要跨线程调用UI控件,通常的解决办法是使用Control类中的Invoke方法,传递给该方法一个委托和委托调用的参数列表(params []object args),任何委托类型都可以,通过委托来访问UI。其内部是,非UI线程把委托送到UI线程中,让UI线程去调用这个委托。

一般一个方法的参数是委托类型,如果使用委托实例,那就还需要额外定义一个和委托签名一致的方法,比较麻烦。而我一般是使用匿名方法,因为匿名方法方便,不需要额外的声明的一个委托类型了,让代码更加简洁。但是这一次使用匿名方法却出现了错误,在编译的时候就没有被通过,我代码如下(code1):

 this.Invoke(delegate(string test) {this.Text=test;},"myCall");

编译时错误如下:

无法将 匿名方法 转换为类型“System.Delegate”,因为它不是委托类型

经过仔细思考,发现这样的写法通过编译确实是对的。首先得明白所有的委托类型都是继承System.Delegate,而Invoke方法委托类型就是Delegate,也就是说,任意的委托类型都可以作为该方法的参数。如果我们使用匿名方法,那么CLR就不知道把该匿名方法转为哪种类型的的委托,因此直接传入一个匿名函数是通不过编译的。知道了为什么错误,代码改成下面这样(code2),就能够正确的运行罗。

Action<string> updateUI = (text) => this.Text = text;
this.Invoke(updateUI,"myCall");
// 或者这样,和上面一样,还是传入Action<string>委托实例
this.Invoke(new Action<string>((text) => this.Text = text), "myCall");

在使用委托类型的时候,建议最好采用系统内部定义Action和Func两种委托类型,而且这两种类型还定义了许多个泛型版本,足够满足我们平常的应用啦。

那么在哪种情况下可以直接传入匿名方法作为参数,举个例子,在使用Linq的扩展Where方法的时候,该方法需要传入一个委托,委托的定义为Func<T,bool>,这种情况我们可以直接使用一个匿名方法,代码如下(code3):

list.Where((s) => s.StartsWith("n"));

因此可以知道,当方法的参数是具体的委托类型时,可以使用匿名方法,因为编译器知道应该把该匿名方法转为何种委托类型;而 当方法的参数为委托的基类System.Delegate时,那就需要自己实例化一个具体委托类型实例(委托类型使用.NET内部定义的,如code2),然后传入进去,这样CLR知道就是何种委托类型。

在C#3.0中有了一个叫Lambda的表达式,和匿名方法是一样效果,但是Lambda能够使代码更加紧凑,更加优异,主要是比较容易理解表达式,可读性好,关于Lambda的用法在这里就不做介绍了。上面的code2和code3的匿名方法都是采用的Lambda表达式,是不是看起来更加简洁。

上一篇:CodeForces #367 div2 C


下一篇:day 67 django 之ORM 增删改查基础