方法组的行为与 lambda 不同?

Method group behaving differently than lambda?

我正在使用 Moq 模拟一些界面。这是:

var titleGenerator = new Mock<ITitleGenerator>();
titleGenerator.Setup(t => t.GenerateTitle()).Returns(Guid.NewGuid().ToString);

Console.WriteLine(titleGenerator.Object.GenerateTitle());
Console.WriteLine(titleGenerator.Object.GenerateTitle());

它打印了两次相同的值。但是如果我用这个替换第二行:

titleGenerator.Setup(t => t.GenerateTitle()).Returns(() => Guid.NewGuid().ToString());

它 returns 每次调用的唯一值。

我一直认为方法组只是lambda表达式的捷径。有什么不同吗?我试着在文档中搜索任何解释。有人可以启发我吗?

看起来方法组对表达式求值一次并以某种方式缓存它?还是跟Moq有什么关系?

在您的第一个示例中,您将传递单个 GuidToString 函数,然后在每次调用时调用该函数。相当于:

Guid guid = Guid.NewGuid();
titleGenerator.Setup(t => t.GenerateTitle()).Returns(guid.ToString)

在您的第二个示例中,您传递的函数首先创建一个新的 Guid,然后对其调用 ToString()

区别在于输入。在第一种情况下,"method group" 实际上是 Guid.ToString 的委托。但由于它需要一个 实例 作为 "input",该实例是委托表达式的一部分,因此每次都使用相同的 "input"。

相当于:

var titleGenerator = new Mock<ITitleGenerator>();
Guid g = Guid.NewGuid();
titleGenerator.Setup(t => t.GenerateTitle()).Returns(g.ToString);

在第二种情况下,代表没有输入Guid 实例在委托 中计算,因此每次都使用一个新的Guid

对于可能更容易理解的等效示例,代码:

var id = 1;
Func<string> f = id.ToString;
id = 2;
Console.WriteLine(f()); // 1

会写成"1",而:

var id = 1;
Func<string> f = () => id.ToString();
id = 2;
Console.WriteLine(f()); // 2

会写"2".

第一种情况 中,委托(Func<> 实例)f 的创建值为 1 作为 Target and the method info for string int.ToString() as Method.稍后重新分配给 id 不会影响 f.

第二种情况中,事情会更加间接。编译器将生成一个对应于 => 箭头的新方法。局部变量idcapturedclosed over(在closure的拉姆达)。这意味着,在幕后,id 确实在某处提升为 field(编译器的选择)。当您的方法提到 id 时,它确实访问了该字段。与 => 箭头对应的编译器生成的方法也读取该字段。现在 Func<> 被创建,其 Method 属性 是编译器生成的方法。由于这一切,这里的结果将是 "2"。这就是 C# 中 匿名函数 的闭包语义。

您原来的最小起订量示例是一样的。有问题的 Returns 重载采用参数 Func<TResult> valueFunction,其中 TResult 在您的使用中是 stringvalueFunction 就是我在更简单的示例中所说的 f