道歉,这是一个很长的描述!
我有一个表示给定值的泛型类。
public class ValueClass<T>
{
public object Value { get { return this._value; } }
protected T _value;
public ValueClass(T value)
{
this._value = value;
}
public string Print()
{
return ((T)this.Value).ToString();
}
}
这可以如图所示:
[TestCase(1, "1")]
[TestCase(2, "2")]
public void Works(int value, string expected)
{
ValueClass<int> uut = new ValueClass<int>(value);
string ret = uut.Print();
Assert.AreEqual(expected, ret);
}
这适用于int
等类型,但如果我想使用自定义类,那么这会失败。例如,对于类型ICustomType
,应该调用ToString方法。
public interface ICustomType
{
string ToString();
}
因此,以下测试失败,其中ICustomType
被模拟:
[TestCase("1")]
[TestCase("2")]
public void Fails(string expected)
{
Mock<ICustomType> customTypeStub = new Mock<ICustomType>();
customTypeStub.Setup(x => x.ToString()).Returns(expected);
ValueClass<ICustomType> uut = new ValueClass<ICustomType>(customTypeStub.Object);
string ret = uut.Print();
Assert.AreEqual(expected, ret);
}
(下面添加了额外的诊断线 - 转换为特定类型,但不是T型)
public class ValueClass<T>
{
public object Value { get { return this._value; } }
protected T _value;
public ValueClass(T value)
{
this._value = value;
}
public string Print()
{
Console.WriteLine("this.Value.ToString() : " + this.Value.ToString());
Console.WriteLine("((ICustomType)this.Value).ToString() : " + ((ICustomType)this.Value).ToString());
Console.WriteLine("((T)this.Value).ToString() : " + ((T)this.Value).ToString());
Console.WriteLine("typeof(T) : " + typeof(T));
Console.WriteLine("(typeof(T) == typeof(ICustomType)) : " + (typeof(T) == typeof(ICustomType)));
return ((T)this.Value).ToString();
}
}
以下诊断信息:
***** tests.Types.Fails("1")
this.Value.ToString() : Castle.Proxies.ICustomTypeProxy
((T)this.Value).ToString() : Castle.Proxies.ICustomTypeProxy
typeof(T) : Types.ICustomType
(typeof(T) == typeof(ICustomType)) : True
***** tests.Types.Fails("2")
this.Value.ToString() : Castle.Proxies.ICustomTypeProxy
((T)this.Value).ToString() : Castle.Proxies.ICustomTypeProxy
typeof(T) : Types.ICustomType
(typeof(T) == typeof(ICustomType)) : True
据我所知,Moq正确地模拟了ToString方法。这在手动转换为固定类型时工作正常。但是,当依赖泛型类型T来定义转换时,这会失败。
请注意,我必须将Value
保留为类型object
而不是类型T
的原因是ValueClass实现了非泛型接口 - 值必须可访问但类型无法定义在接口级别。
谁能解释这种行为?
答案 0 :(得分:1)
这里的问题是编译器不知道你打算给它一个接口来指示它使用不同的 ToString方法,而不是每个对象拥有的方法。
编译器唯一知道的T
是某种类型。编译器将在编译时使用它具有的知识编译该方法,即使您稍后给它一个实际上告诉它使用不同ToString
方法的接口,它也不会使用它,因为它已编译所有类型的方法,该编译使用System.Object
提供的方法。
所以,你不能这样做。
您可以指示您的ValueClass
仅支持实现您的界面的T
类型,但我怀疑这不是您想要的。
以下是Print方法的编译方式:
ValueClass`1.Print:
IL_0000: ldarg.0
IL_0001: call 15 00 00 0A
IL_0006: unbox.any 05 00 00 1B
IL_000B: stloc.0 // CS$0$0000
IL_000C: ldloca.s 00 // CS$0$0000
IL_000E: constrained. 05 00 00 1B
IL_0014: callvirt System.Object.ToString
IL_0019: ret
正如您所看到的,它被编译为直接调用System.Object.ToString
,显然您可以在提供给T
的实际类型中覆盖,但编译器会不明白你在某些情况下打算给它一个带有自己的ToString
方法的接口,因此不会通过接口调用方法。 Moq创建的Mock对象创建了ToString
的显式实现,并且不会覆盖从System.Object
继承的实现,因此您会得到错误/意外的结果。