我正在使用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());
它会在每次调用时返回唯一值。
我一直以为方法组只是lambda表达式的快捷方式。有什么区别吗?我试着在文档中搜索任何解释。有人可以开导我吗?
看起来方法组只评估表达式并以某种方式缓存它?或者它与Moq
有什么关系?
答案 0 :(得分:9)
在第一个示例中,您传递了单个ToString
的{{1}}函数,然后在每次调用时调用它。它等同于:
Guid
在第二个示例中,您传递的函数首先创建一个新的Guid guid = Guid.NewGuid();
titleGenerator.Setup(t => t.GenerateTitle()).Returns(guid.ToString)
,然后在其上调用Guid
。
答案 1 :(得分:0)
区别在于输入。在第一种情况下,“方法组”实际上是Guid.ToString
的委托。但由于它需要实例作为“输入”,因此实例是委托表达式的一部分,因此每次都使用相同的“输入”。
这相当于:
var titleGenerator = new Mock<ITitleGenerator>();
Guid g = Guid.NewGuid();
titleGenerator.Setup(t => t.GenerateTitle()).Returns(g.ToString);
在第二种情况下,委托没有输入。 Guid
实例在委托中计算,因此每次都会使用新的Guid
。
答案 2 :(得分:0)
对于可能更容易理解的等效示例,代码:
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
,方法信息string int.ToString()
为Method
。稍后重新分配到id
不会影响f
。
在第二种情况中,事情会更加间接。编译器将生成一个与=>
箭头对应的新方法。本地变量id
已捕获或已关闭(位于lambda的闭包中)。这意味着,在幕后,id
实际上被提升为某个地方的field
(编译器的选择)。当您的方法提到id
时,它确实会访问该字段。并且与=>
箭头对应的编译器生成的方法也读取该字段。现在创建Func<>
,其Method
属性是此编译器生成的方法。因此,结果将是"2"
。这是C#中匿名函数的闭包语义。
你原来的Moq示例是一样的。有问题的Returns
重载需要参数Func<TResult> valueFunction
,其中TResult
为string
。在我更简单的示例中,valueFunction
就是我所谓的f
。