21

我是所有匿名功能的新手,需要一些帮助。我已经完成了以下工作:

public void FakeSaveWithMessage(Transaction t)
{
    t.Message = "I drink goats blood";
}

public delegate void FakeSave(Transaction t);

public void SampleTestFunction()
{
    Expect.Call(delegate { _dao.Save(t); }).Do(new FakeSave(FakeSaveWithMessage));
}

但这完全是丑陋的,如果可能的话,我希望 Do 的内部成为一个匿名方法,甚至是一个 lambda。我试过:

Expect.Call(delegate { _dao.Save(t); }).Do(delegate(Transaction t2) { t2.Message = "I drink goats blood"; });

Expect.Call(delegate { _dao.Save(t); }).Do(delegate { t.Message = "I drink goats blood"; });

但这些给了我

无法将匿名方法转换为类型“System.Delegate”,因为它不是委托类型** 编译错误。

我究竟做错了什么?


由于 Mark Ingram 发布的内容,尽管没有人明确表示,但似乎最好的答案是这样做:

public delegate void FakeSave(Transaction t);

Expect.Call(delegate { _dao.Save(t); }).Do( new FakeSave(delegate(Transaction t2) { t.Message = expected_msg; }));
4

4 回答 4

27

这是一个众所周知的错误消息。检查下面的链接以获得更详细的讨论。

http://staceyw1.wordpress.com/2007/12/22/they-are-anonymous-methods-not-anonymous-delegates/

基本上你只需要在你的匿名委托(你的 lambda 表达式)前面加上一个演员。

万一链接断开,这里是帖子的副本:

它们是匿名方法,而不是匿名委托。
发表于 2007 年 12 月 22 日由 staceyw1

这不仅仅是一个话题,因为我们想要变得困难。它帮助我们推理到底发生了什么。需要明确的是,*没有匿名代表之类的东西。它们不存在(还不存在)。它们是“匿名方法”——句号。重要的是我们如何看待它们以及我们如何谈论它们。让我们看一下匿名方法语句“delegate() {…}”。这实际上是两种不同的操作,当我们这样想的时候,我们就再也不会感到困惑了。编译器做的第一件事是使用推断的委托签名作为方法签名在幕后创建匿名方法。说该方法是“未命名的”是不正确的,因为它确实有一个名称并且编译器分配了它。它只是从正常视图中隐藏。它要做的下一件事是创建一个所需类型的委托对象来包装该方法。这称为委托推理,可能是这种混乱的根源。为此,编译器必须能够弄清楚(即推断)它将创建什么委托类型。它必须是已知的具体类型。让我们写一些代码来看看为什么。

private void MyMethod()
{
}

不编译:

1) Delegate d = delegate() { };                       // Cannot convert anonymous method to type ‘System.Delegate’ because it is not a delegate type
2) Delegate d2 = MyMethod;                         // Cannot convert method group ‘MyMethod’ to non-delegate type ‘System.Delegate’
3) Delegate d3 = (WaitCallback)MyMethod;   // No overload for ‘MyMethod’ matches delegate ‘System.Threading.WaitCallback’

第 1 行无法编译,因为编译器无法推断任何委托类型。它可以清楚地看到我们想要的签名,但是编译器看不到具体的委托类型。它可以为我们创建一个匿名类型的类型委托,但它不是那样工作的。出于类似原因,第 2 行无法编译。即使编译器知道方法签名,我们也不会给它一个委托类型,它不会只是选择一个会发生工作的方法(而不是可能有什么副作用)。第 3 行不起作用,因为我们故意将方法签名与具有不同签名的委托不匹配(因为 WaitCallback 采用和对象)。

编译:

4) Delegate d4 = (MethodInvoker)MyMethod;  // Works because we cast to a delegate type of the same signature.
5) Delegate d5 = (Action)delegate { };              // Works for same reason as d4.
6) Action d6 = MyMethod;                                // Delegate inference at work here. New Action delegate is created and assigned.

相比之下,这些工作。第 1 行有效,因为我们告诉编译器使用什么委托类型并且它们匹配,所以它有效。第 5 行的工作原理相同。请注意,我们使用了不带括号的特殊形式的“委托”。编译器从强制转换推断方法签名,并创建与推断的委托类型具有相同签名的匿名方法。第 6 行有效,因为 MyMethod() 和 Action 使用相同的签名。

我希望这有帮助。

另见:http: //msdn.microsoft.com/msdnmag/issues/04/05/C20/

于 2008-09-12T17:20:21.537 回答
3

马克说的。

问题是 Do 需要一个 Delegate 参数。编译器不能将匿名方法转换为委托,只能转换为“委托类型”,即从委托派生的具体类型。

如果该 Do 函数采用了 Action<>、Action<、> ... 等重载,则不需要强制转换。

于 2008-09-12T17:27:24.950 回答
1

问题不在于您的委托定义,而在于 Do() 方法的参数是 System.Delegate 类型,并且编译器生成的委托类型 (FakeSave) 不会隐式转换为 System.Delegate。

尝试在您的匿名代表面前添加演员表:

Expect.Call(delegate { _dao.Save(t); }).Do((Delegate)delegate { t.Message = "I drink goats blood"; });
于 2008-09-12T17:47:48.723 回答
0

尝试类似:

Expect.Call(delegate { _dao.Save(t); }).Do(new EventHandler(delegate(Transaction t2) { t2.CheckInInfo.CheckInMessage = "I drink goats blood"; }));

请注意在委托周围添加的 EventHandler。

编辑:可能不起作用,因为 EventHandler 和委托的函数签名不同......您添加到问题底部的解决方案可能是唯一的方法。

或者,您可以创建一个通用委托类型:

public delegate void UnitTestingDelegate<T>(T thing);

因此委托不是特定于事务的。

于 2008-09-12T17:07:57.587 回答