为什么在 xunit.net 理论中不能将 List<int> 转换为 TCollection?

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 使用已解决的泛型通过反射。

Ert*_*maa 2

无论你做什么,它仍然归结为一个事实:在幕后正在进行转换IEnumerable<int>IEnumerable<object>

您似乎假设通用参数是根据给定的具体输入填充的。

事实并非如此。泛型类型参数是从 中引用的变量推导出来的MemberData,而不是它所保存的实际内容,因此,您的测试方法将始终使用下一个签名进行调用:

 AddRangeTest2<object, object[]>(object[] items)
Run Code Online (Sandbox Code Playgroud)

上面的签名支持引用类型的隐式协变(意味着:您可以传递给此方法,但不能)。string[]int[]

不支持值类型的协方差


也就是说,如果 xUnit 足够聪明,能够根据实际输入指定正确的泛型类型参数,那就太酷了。我自己没有使用 xUnit 的经验,也许已经有办法了?如果没有,请在 Codeplex 中提出功能请求,或者自己制作+拉取请求;)