将此委托转换为匿名方法或者lambda

时间:2020-03-05 18:52:23  来源:igfitidea点击:

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

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"; });

但是这些给了我

Cannot convert anonymous method to type 'System.Delegate' because it is not a delegate type** compile errors.

我究竟做错了什么?

由于马克·英格拉姆(Mark Ingram)发布的内容,这似乎是最好的答案,尽管没有人明确表示是这样做的:

public delegate void FakeSave(Transaction t);

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

解决方案

回答

尝试类似的东西:

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);

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

回答

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

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

基本上,我们只需要在匿名代表(lambda表达式)前面放置一个强制转换。

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

They are Anonymous Methods, not
  Anonymous Delegates.

  Posted on December 22, 2007 by staceyw1  
  
  It is not just a talking point because
  we want to be difficult. It helps us
  reason about what exactly is going on.
  To be clear, there is *no such thing
  as an anonymous delegate. They don’t
  exist (not yet).  They are "Anonymous
  Methods" – period.  It matters in how
  we think of them and how we talk about
  them.  Lets take a look at the
  anonymous method statement "delegate()
  {…}".  This is actually two different
  operations and when we think of it
  this way, we will never be confused
  again.  The first thing the compiler
  does is create the anonymous method
  under the covers using the inferred
  delegate signature as the method
  signature.  It is not correct to say
  the method is "unnamed" because it
  does have a name and the compiler
  assigns it. It is just hidden from
  normal view.  The next thing it does
  is create a delegate object of the
  required type to wrap the method. This
  is called delegate inference and can
  be the source of this confusion. For
  this to work, the compiler must be
  able to figure out (i.e. infer) what
  delegate type it will create. It has
  to be a known concrete type.  Let
  write some code to see why.
private void MyMethod()
{
}
Does not compile:
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’
Line 1 does not compile because the
  compiler can not infer any delegate
  type. It can plainly see the signature
  we desire, but there is no concrete
  delegate type the compiler can see. 
  It could create an anonymous type of
  type delegate for us, but it does not
  work like that.  Line 2 does not
  compile for a similar reason. Even
  though the compiler knows the method
  signature, we are not giving it a
  delegate type and it is not just going
  to pick one that would happen to work
  (not what side effects that could
  have).  Line 3 does not work because
  we purposely mismatched the method
  signature with a delegate having a
  different signature (as WaitCallback
  takes and object).
  
  Compiles:
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.
In contrast, these work. Line 1 works
  because we tell the compiler what
  delegate type to use and they match,
  so it works.  Line 5 works for the
  same reason. Note we used the special
  form of "delegate" without the parens.
  The compiler infers the method
  signature from the cast and creates
  the anonymous method with the same
  signature as the inferred delegate
  type. Line 6 works because the
  MyMethod() and Action use same
  signature.
  
  I hope this helps.
  
  Also see:
  http://msdn.microsoft.com/msdnmag/issues/04/05/C20/

回答

马克说了什么。

问题是Do接受一个Delegate参数。编译器不能将匿名方法转换为Delegate,只能将其转换为Delegate类型,即从Delegate派生的具体类型。

如果该Do函数已使Action <>,Action <,> ...等重载,则不需要进行强制转换。

回答

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

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

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