feO*_*O2x 5 c# generics xunit xunit.net
我正在尝试在 xunit.net 中编写一个通用理论,该理论通过MemberDataAttribute. 请看下面的代码:
[Theory]
[MemberData("TestData")]
public void AddRangeTest<T>(List<T> items)
{
var sut = new MyCustomList<T>();
sut.AddRange(items);
Assert.Equal(items, sut);
}
public static readonly IEnumerable<object[]> TestData = new[]
{
new object[] { new List<int> { 1, 2, 3 } },
new object[] { new List<string> { "Foo", "Bar" } }
};
Run Code Online (Sandbox Code Playgroud)
当我尝试执行这个理论时,会抛出以下异常:“System.ArgumentException: Object of type 'List'1[System.String] / List'1[System.Int32]' cannot be convert to type 'List'[ System.Object]'。” (我缩短并合并了两个例外的文本)。
我知道这可能与参数类型有关,因为它不是直接的泛型类型,而是使用嵌套泛型的类型。因此,我以以下方式转换了测试:
[Theory]
[MemberData("TestData")]
public void AddRangeTest2<T, TCollection>(TCollection items)
where TCollection : IEnumerable<T>
{
var sut = new MyCustomList<T>();
sut.AddRange(items);
Assert.Equal(items, sut);
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我引入了一个TCollection被限制为IEnumerable<T>实例的泛型调用。当我执行此测试时,运行 withList<string>有效,但运行 withList<int>产生以下异常:“System.ArgumentException: GenericArguments[1], 'List'1[System.Int32]', on 'AddRangeTest2'违反了类型的约束'TCollection'。” (同样,我将异常文本缩短为相关要点)。
我的实际问题是:为什么可以在泛型理论中使用List<string>但不能使用List<int>?TCollection在我看来,这两种类型都满足对泛型的约束。
如果我更改List<int>为List<object>,则一切正常 - 因此我认为它与值类型/引用类型和协方差/逆变有关。
我使用 xunit 2.0.0-rc2 虽然我也用 rc1 观察到这种行为 - 因此我认为它与版本无关(甚至可能不是 xunit 的问题)。如果您想查看完整的源代码,可以从我的保管箱下载(我是用 VS 2013 创建的)。请将此代码视为MCVE。
非常感谢您的帮助!
关闭此问题后进行编辑:我的问题没有被Cannot convert from List<DerivedClass> to List<BaseClass>回答,因为我在这里根本不使用基于继承的强制转换 - 相反,我相信 xunit.net 使用已解决的泛型通过反射。
无论你做什么,它仍然归结为一个事实:在幕后正在进行转换IEnumerable<int>。IEnumerable<object>
您似乎假设通用参数是根据给定的具体输入填充的。
事实并非如此。泛型类型参数是从 中引用的变量推导出来的MemberData,而不是它所保存的实际内容,因此,您的测试方法将始终使用下一个签名进行调用:
AddRangeTest2<object, object[]>(object[] items)
Run Code Online (Sandbox Code Playgroud)
上面的签名支持引用类型的隐式协变(意味着:您可以传递给此方法,但不能)。string[]int[]