为什么我不能对/用于ThreadStart使用/转换Action?

时间:2010-03-21 20:46:44

标签: c# multithreading delegates action

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

为什么?

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

t = new Thread(doIt);
t.Start();

但这似乎有效:

Thread t;

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

8 个答案:

答案 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();