我正在尝试创建一个在测试中使用的伪对象缓存类,我遇到了奇怪的行为。
我从这开始:
IObjectCache objectCache = A.Fake<IObjectCache>();
List<ICachedObject> objects = new List<ICachedObject>();
ICachedObject object1 = A.Fake<ICachedObject>();
A.CallTo(() => object1.Id).Returns(random.Next());
objects.Add(object1);
//Snip: Identical statements for object2 & object3...
A.CallTo(objectCache)
.Where(call => call.Method.Name == "get_Item")
//"get_Item" is the generated name for the indexer []
.WithReturnType<ICachedObject>()
.WhenArgumentsMatch((int objectId) => objectId.Equals(object1.Id))
.Returns(object1);
//Snip: Identical statements for object2 & object3...
//The lookup works as expected:
var x = objectCache[object1.Id]; //returns object1
var y = objectCache[object2.Id]; //returns object2
var z = objectCache[object3.Id]; //returns object3
在开始工作之后,我想通过移动&#34; CallTo&#34;来清理测试。循环语句。但是,当我这样做时,假的不再返回预期的对象。
foreach (ICachedObject obj in objects)
{
A.CallTo(objectCache)
.Where(call => call.Method.Name == "get_Item")
//"get_Item" is the generated name for the indexer []
.WithReturnType<ICachedObject>()
.WhenArgumentsMatch((int objectId) => objectId.Equals(obj.Id))
.Returns(obj);
}
//Only the last object "added" works
var x = objectCache[object1.Id]; //returns a new, empty object
var y = objectCache[object2.Id]; //returns a new, empty object
var z = objectCache[object3.Id]; //returns object3
我想知道是否可能是WhenArgumentsMatch&amp;在索引器查找调用它们之前不会计算返回值,并且只能保存一个值。还有另一种方法吗?
答案 0 :(得分:2)
我无法重现您的问题,但我有一种强烈的怀疑......您使用的是C#4还是更早?在C#5之前,foreach
循环中的循环变量在逻辑上声明在循环之外,所以如果你在lambda中捕获它,你总是引用相同的变量。这意味着当评估lambda (int objectId) => objectId.Equals(obj.Id)
时,obj
始终引用列表中的最后一项。此行为在C#5中已更改,因此循环变量在逻辑上声明在循环中,这可以防止出现这种令人惊讶的行为(有关详细信息,请参阅Eric Lippert的this article)。
如果确实是您遇到问题的原因,请在循环中复制obj
:
foreach (ICachedObject obj in objects)
{
var copy = obj;
A.CallTo(objectCache)
.Where(call => call.Method.Name == "get_Item")
//"get_Item" is the generated name for the indexer []
.WithReturnType<ICachedObject>()
.WhenArgumentsMatch((int objectId) => objectId.Equals(copy.Id))
.Returns(copy);
}
顺便说一句,您可以更轻松地配置这些调用:
foreach (var obj in objects)
{
var copy = obj;
A.CallTo(() => objectCache[copy.Id]).Returns(copy);
}