两者都是委托并具有相同的签名,但我不能将Action用作ThreadStart。
为什么?
Action doIt;
doIt = () => MyMethod("test");
Thread t;
t = new Thread(doIt);
t.Start();
但这似乎有效:
Thread t;
t = new Thread(() => MyMethod("test"));
t.Start();
答案 0 :(得分:21)
正如其他人所指出的,问题是委托类型不是“结构性的”。也就是说,它们没有基于其“结构”的等价。
现在,对于某些类型来说,这可能是一件好事。如果你有
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<int, bool>
转换为Predicate<int>
等等。
所以,有两件事,一件是向后看,另一件是向前看。倒退:如果我们不得不重新做一遍,我认为代表可能会在CLI中进行结构化输入。在设计一个全新的框架时,你并不总是知道哪些特性会有用,而迄今为止,非结构化的委托类型并不像预期的那样有用。前瞻:我希望在未来版本的CLR中看到更多功能,以实现更多的结构类型。例如,C#4中的“no pia”功能是关于使两种语义和结构相同的类型,但在不同的程序集中定义,在逻辑上在结构上统一。
答案 1 :(得分:4)
您注意到的行为是因为Action
是一种类型而第二个工作示例中的“lambda”不是。
可以说,它们是相同的,但是在使用Action
时使用的是具体的框架类型,而编译器则推断出lambda的签名。
编辑:要明确:
Action是一个委托类型,它是not
一个ThreadStart委托,所以虽然你可以将lambda表达式分配给两者,但你不能同时使用它们来启动一个线程。
在第二个示例中,您将为编译器提供推断委托类型的机会,并且毫不奇怪,它能够将lamda表达式分配给ThreadStart类型。
答案 2 :(得分:3)
具有相同签名的代表在CLR眼中并不相同 - 它们是完全不同的类型。
答案 3 :(得分:3)
此错误的基本形式是:
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);
答案 4 :(得分:3)
我相信这应该有用吗?
Action doIt;
doIt = () => MyMethod("test");
Thread t;
t = new Thread(doIt.Invoke);
t.Start();
答案 5 :(得分:2)
我认为C#语言规范第26.3.1节中的以下措辞非常重要:
类似于 anonymous-method-expression,a lambda-expression被归类为a 具有特殊转换规则的值。 该值没有类型但可以 被隐式转换为 兼容的委托类型。 具体来说,代表类型D是 与lambda-expression L兼容 提供:
[名单已删除]
这会阻止这样的代码编译:
var doIt = () => MyMethod("test"); // CS0815
导致:
Action<object> doIt = (o) => MyMethod("test");
t = new Thread((ParameterizedThreadStart)doIt); // CS0030
编译器不允许将转换从一种委托类型转换为另一种委托类型,即使它们的签名是兼容的。迫使:
ParameterizedThreadStart doIt = (o) => MyMethod("test");
t = new Thread(doIt);
编译没有问题。
不相关的奖励功能:第一台Apple Mac上的早期可用性测试发现使用sans-serif字体的对话框上的第一个OK按钮版本存在问题。用户经常点击取消,但有一些明显的痛苦。经过几次采访,一位用户终于承认了这个问题:“当电脑叫我Dolt时,我讨厌它!”
好吧,编译器称你为dolt也许并不是那么无关紧要:)
答案 6 :(得分:1)
尝试:
t = new Thread(new ThreadStart(doIt));
或
t = new Thread( ()=>MyMethod("test"));
您正在尝试将类型为“Action”的参数传递给采用ThreadStart的构造函数。没有定义隐式转换,因此您需要手动调用ThreadStart构造函数。
答案 7 :(得分:1)
因为线程启动是一个单独的委托类型,Action
无法转换为ThreadStart
。
这种情况有效,因为这里你的lambda被编译器视为ThreadStart:
Thread t;
t = new Thread(() => MyMethod("test"));
t.Start();