使用泛型类型强制转换模拟
本文关键字:转换 模拟 泛型类型 | 更新日期: 2023-09-27 18:05:27
抱歉,这是一个很长的描述!
我有一个表示给定值的泛型类。
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实现了一个非泛型接口-值必须是可访问的,但类型不能在接口级别定义。有人能解释一下这种行为吗?
这里的问题是编译器不知道你打算给它一个接口,指示它使用一个不同于每个对象的 ToString方法。
编译器唯一知道的是T
是某种类型。编译器将在编译时使用它所拥有的知识编译该方法,即使您稍后给它一个接口,实际上告诉它使用不同的ToString
方法,它也不会使用它,因为它已经为所有类型编译了该方法,并且该编译使用了System.Object
提供的方法。
所以,你不能这样做。
你可以指示你的ValueClass
只支持T
实现你的接口的类型,但我怀疑这不是你想要的。
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
继承的对象,因此您会得到不正确/意外的结果。