2

我之前问过一个关于如何在测试方法时设置接口值的问题 。我已经成功地将 Moq 框架实施到我的项目中,并且测试运行良好。

这是我推荐的示例代码:

public void PostEvent(
            eVtCompId inSenderComponentId, 
            eVtEvtId inEventId, 
            long inEventReference, 
            IF_SerializableData inEventData)
{
    if(mEventMap.ContainsKey(inEventId))
    {
        mEventMap[inEventId](inSenderComponentId, inEventReference, inEventData);
    }
}

这里我有 4 个参数:第一个:一个枚举,第二个:另一个枚举,第三个:long,第四个:一个接口。但是,我误认为第四个参数(接口)不应该是接口,而是reference接口。

所以它应该是这样的:

public void PostEvent(
       eVtCompId inSenderComponentId, 
       eVtEvtId inEventId, 
       long inEventReference, 
       ref IF_SerializableData inEventData)

给我的示例起订量测试代码(就是这个)......

var serializable = new Mock<IF_SerializableData>();
target.PostEvent(..., serializable.Object);

...不起作用。我已经尝试过ref serializable.Object,但它仍然不起作用,因为我收到一个错误,指出 ref 参数期望引用一个变量,而不是一个对象。

关于如何正确测试它的任何提示或示例?

4

1 回答 1

3

您需要将模拟中的Object引用复制serializable到局部变量中,然后您可以将其作为ref.

IF_SerializableData localRef = serializable.Object;
target.PostEvent(..., ref localRef);

您不能传递ref Serializable.Object,因为它是一个属性 - 另请参阅是否可以将属性作为“out”或“ref”参数传递?它很好地讨论了为什么会这样,以及其他链接的来源。

我的解释

这最终是因为属性不是变量。读/写属性是一对提供类似变量功能的访问器方法,但至关重要的是,当您get使用属性时,您始终会获得底层变量的副本——即使该变量具有引用类型。setget

所以:

public class MyClass {
  private object _property;
  public object Property {
    get { return _property; } //<-- _property reference copied on to stack & returned
                              //    here as a new variable.  Therefore a ref
                              //    on that is effectively meaningless.
                              //    for a ref to be possible you'd need a pointer 
                              //    to the _property variable (in C++ terms)
    set { _property = value; }
  }
}

在这个例子中——如果你可以传递ref MyClass.Property给一个方法——这将是没有意义的,因为它将传递一个对堆栈上的瞬态变量的引用——即,Property get accessor; it would not be passing the property by reference. So C# doesn't allow it, even though it could, because it would imply that属性返回的引用的副本可以被方法修改——当它根本做不到。

因此,在您的情况下,为什么我们需要从堆栈中捕获该值并将其复制到局部变量中。现在 - 请注意,为了让该ref方法设置的新值出现在您的身上,Mock<T>您需要再次将其Object属性设置为局部变量值(如果可以的话 - 我不使用 Moq 但我认为它的 Mocks 是不可变的)。

关于 C# 是否应该以这种方式自动处理已经有很多争论ref Property(参见前面提到的我链接到的 SO)。在我看来,这类似于ref Derived不兼容ref Base- 是的,有一种方法可以让语言自动为你处理这个问题,但应该这样做吗?在我看来,没有。我会因此感到沮丧吗?哦,是的,当然 - 但我经常发现它突出了确实应该修复的架构弱点(例如,依赖refout返回值可能更好的参数)。

C# 允许您通过引用传递属性的唯一方法是将 get 和 set 访问器传递给目标方法 - 这根本不兼容ref(因为这只是一个内存位置)。

作为品尝者,您必须编写这样的方法:

 public static void MyUglyRefMethod(Func<object> refGet, Action<object> refSet)
 {
   var read = refGet();
   var newValue = new object();
   refSet(newValue);
 }

有了这个,我们现在可以提供ref类似的语义MyClass

 MyClass a = new MyClass();
 MyUglyRefMethod(() => a.Property, (newValue) => a.Property = newValue);
 Assert.IsNotNull(a.Property);

但这简直丑得要命。

让一个方法更简单ref MyClass,然后它可以直接写入任何属性。

于 2012-12-11T08:53:12.367 回答