为什么我不能使用/强制转换/为ThreadStart?

两者都是委托并具有相同的签名,但我不能将Action用作ThreadStart。

为什么?

Action doIt; doIt = () => MyMethod("test"); Thread t; t = new Thread(doIt); t.Start(); 

但这似乎有效:

 Thread t; t = new Thread(() => MyMethod("test")); t.Start(); 

正如其他人所指出的那样,问题是委托类型不是“结构性的”。 也就是说,它们没有基于其“结构”的等价。

现在,对于某些类型来说,这可能是一件好事。 如果你有

 struct MyRectangle { int x; int y; int width; int height; ... } 

 struct YourRectangle { int x1; int y1; int x2; int y2; ... } 

显然,允许将MyRectangle的实例分配给YourRectangle的变量是错误的,因为它们都由四个整数组成。 整数的语义不同,因此类型不等同。

从理论上讲,对代表来说也是如此。 你可以有

 delegate int Pure(string x); delegate int Func(string x); 

其中“纯”函数是没有副作用的函数,并且在给定相同输入的情况下输出相同。 由于每个Pure在逻辑上都是Func,但每个Func不一定是Pure,因此它们之间不应该有结构类型。

在实践中,类型系统当然不能很好地支持像“纯函数”这样的概念。 实际上,绝大多数在委托类型之间进行转换的尝试都非常安全:从Func转换为Predicate等等。

所以,有两件事,一件是向后看,另一件是向前看。 倒退:如果我们不得不重新做一遍,我认为代表可能会在CLI中进行结构化输入。 在设计一个全新的框架时,你并不总是知道哪些特性会有用,而迄今为止,非结构化的委托类型并不像预期的那样有用。 前瞻:我希望在未来版本的CLR中看到更多function,以实现更多的结构类型。 例如,C#4中的“无pia”特征是关于使两种类型在语义上和结构上相同,但在不同的集合中定义,在逻辑上在结构上统一。

你注意到的行为是因为Action是一个类型而你的第二个工作示例中的’lambda’不是。

可以说,它们是相同的,但是在使用Action时使用的是具体的框架类型,而编译器则推断出lambda的签名。

编辑:要清楚:

Action是一个委托类型,它not ThreadStart委托,因此虽然您可以将lambda表达式分配给两者,但不能同时使用它们来启动一个线程。

在第二个示例中,您将为编译器提供推断委托类型的机会,并且毫不奇怪,它能够将lamda表达式分配给ThreadStart类型。

具有相同签名的代表在CLR眼中并不相同 – 它们是完全不同的类型。

我相信这应该有效吗?

 Action doIt; doIt = () => MyMethod("test"); Thread t; t = new Thread(doIt.Invoke); t.Start(); 

此错误的基本forms是:

 delegate void d1(); delegate void d2(); d1 a; d2 b; b = a; Error Cannot implicitly convert type 'ConsoleDemo1.d1' to 'ConsoleDemo1.d2' 

因此,您可以使用正确的委托类型“解决”您的第一个样本:

 //Action doIt; ThreadStart doIt; doIt = () => MyMethod("test"); Thread t = new Thread(doIt); 

我认为C#语言规范第26.3.1节中的以下措辞非常重要:

与anonymous-method-expression类似,lambda表达式被分类为具有特殊转换规则的值。 该值没有类型,但可以隐式转换为兼容的委托类型。 具体来说,委托类型D与提供的lambda表达式L兼容:
[名单已删除]

这会阻止这样的代码编译:

 var doIt = () => MyMethod("test"); // CS0815 

这导致:

 Action doIt = (o) => MyMethod("test"); t = new Thread((ParameterizedThreadStart)doIt); // CS0030 

编译器不允许将转换从一种委托类型转换为另一种委托类型,即使它们的签名是兼容的。 强制:

 ParameterizedThreadStart doIt = (o) => MyMethod("test"); t = new Thread(doIt); 

哪个编译没有问题。


无关的奖励function:第一台Apple Mac上的早期可用性测试发现使用sans-serif字体的对话框上的第一个OK按钮版本存在问题。 用户经常点击取消,但有一些明显的痛苦。 经过几次采访,一位用户终于承认了这个问题:“当电脑叫我Dolt时,我讨厌它!”

好吧,编译器称你为dolt也许并不是那么无关紧要:)

尝试:

 t = new Thread(new ThreadStart(doIt)); 

要么

 t = new Thread( ()=>MyMethod("test")); 

您正在尝试将类型为“Action”的参数传递给采用ThreadStart的构造函数。 没有定义隐式转换,因此您需要手动调用ThreadStart构造函数。

因为线程启动是一个单独的委托类型而Action不能转换为ThreadStart

这种情况有效,因为这里你的lambda被编译器视为ThreadStart:

 Thread t; t = new Thread(() => MyMethod("test")); t.Start();