无法将泛型接口的具体实例添加到泛型集合中。

5
我会让代码自己说话:
using System.Collections.Generic;

namespace test
{
    public interface IThing { } // can't change this - it's a 3rd party thing

    public interface IThingRepository<T> where T : class, IThing { } // can't change this - it's a 3rd party thing

    public interface IThingServiceInstance<T>
      where T : class, IThing
    {
        IThingRepository<T> Repository { get; set; }
    }

    public class ThingServiceInstance<T> : IThingServiceInstance<T> where T : class, IThing
    {
        public IThingRepository<T> Repository { get; set; }

    }

    public class MyThing : IThing
    {
    }

    class Test
    {
        public void DoStuff()
        {
            IList<IThingServiceInstance<IThing>> thingServiceInstances = new List<IThingServiceInstance<IThing>>();
            // the following line does not compile. Errors are:
            // 1: The best overloaded method match for 'System.Collections.Generic.ICollection<test.IThingServiceInstance<test.IThing>>.Add(test.IThingServiceInstance<test.IThing>)' has some invalid arguments    C:\TFS\FACE\ResearchArea\ArgonServiceBusSpike\Argon_Service_Bus_Spike_v2\Argon.ServiceLayer\test.cs 31  13  Argon.ServiceGateway
            // 2: Argument 1: cannot convert from 'test.ThingServiceInstance<test.MyThing>' to 'test.IThingServiceInstance<test.IThing>'    C:\TFS\FACE\ResearchArea\ArgonServiceBusSpike\Argon_Service_Bus_Spike_v2\Argon.ServiceLayer\test.cs 31  39  Argon.ServiceGateway
            // Why? ThingServiceInstance is an IThingServiceInstance and MyThing is an IThing
            thingServiceInstances.Add(new ThingServiceInstance<MyThing>());
        }
    }
}

如果ThingServiceInstance是一个IThingServiceInstance,并且MyThing是一个IThing,为什么我不能将ThingServiceInstance<MyThing>添加到IThingServiceInstance<IThing>的集合中呢?

我该如何使此代码编译?


泛型中的协变性和逆变性 - Jehof
使用关键词“协变和逆变”进行搜索。 - Sriram Sakthivel
2个回答

3

ThingServiceInstance<MyThing> 不是 IThingServiceInstance<IMyThing> 的子类型,因为 IThingServiceInstance<T> 在其类型参数 <T> 上是不变的

如果你想让 ThingServiceInstance<MyThing> 成为 IThingServiceInstance<IMyThing> 的子类型,则必须使 T 协变。 在 C# 中,可以通过将 IThingServiceInstance<T> 声明为以下方式来实现:

public interface IThingServiceInstance<out T>

编辑 这意味着ThingServiceInstance<T>只能返回T的实例,但永远不能将它们作为方法参数(因此有了“out”符号)。

编辑2

这就是为什么您的代码无法编译的要点。正如指出的那样,由于您的ThingServiceInstance<T>公开了一个IThingRepository<T>属性,因此也必须是协变的,如下所示:

public interface IThingRepository<out T> where T : class, IThing { }

如下,你的属性必须是只读的(记住,你只能返回 T 的实例,或者同样的 U<T>)。

你需要更改两个接口,使它们都具有协变的 T。目前来看,你的更改无法通过编译,出现了 Invalid variance: The type parameter 'T' must be invariantly valid on 'IThingServiceInstance<T>.Repository'. 'T' is covariant. 的错误提示。 - Rob

1
如果您将两个接口声明为协变,并从 IThingServiceInstance 中删除 setter,那么就可以让它编译通过。当然,如果无法更改第三方接口,则这并没有什么帮助。
public interface IThingRepository<out T> where T : class, IThing { } // can't change this - it's a 3rd party thing

public interface IThingServiceInstance<out T>
  where T : class, IThing
{
    IThingRepository<T> Repository { get; }
}

如果 IThingRepository<T> 没有声明 T 协变,那么对于
T : A

你不理解
IThingRepository<T> : IThingRespository<A>

因此你不能拥有

IThingServiceInstance<T> : IThingServiceInstance<A>

由于getter返回的类型不"兼容"。

1
这是“协变”的概念,不是“逆变”。另外,您无需删除setter。 - D Stanley
@D Stanley:如果我保留setter,就会出现编译错误:“无效的方差:类型参数'T'必须在'IThingServiceInstance<T>.Repository'上逆变有效。'T'是协变的。” - Rob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接