为什么这个泛型方法调用不匹配?

时间:2015-05-12 06:29:05

标签: c# generics

我有一个Address课程:

public class Address
{
    //Some stuff
}

并且有相应的*Wrapper类来强制执行有关如何操作的某些规则 使用Address类:

public class AddressWrapper : IWrapped<Address>
{
    private Address _wrapped;

    public Address GetWrapped()
    {
        return _wrapped;
    }

    //And some more
}

其中IWrapped定义为:

public interface IWrapped<T>
{
    T GetWrapped();
}

我有以下用于保存这些实体的通用类(还有其他类 遵循此EntityEntityWrapper)模式的实体:

public class GenericRepository
{
    private GenericRepository() { }

    public static void Add<T>(IWrapped<T> entity)
    {
        //Do something
    }

    public static void AddList<T>(IList<IWrapped<T>> entities)
    {
        //Do something
    }
}

我有这个测试代码:

[Test]
public void UseGenericRepository()
{
    AddressWrapper addrW = new AddressWrapper();
    addrW.AddrLine1 = "x";
    addrW.AddrLine2 = "y";
    addrW.AddrLine3 = "z";
    addrW.City = "Starling City";
    //This works as expected
    GenericRepository.Add<Address>(addrW);

    IList<AddressWrapper> addrList = new List<AddressWrapper>();
    //Fill up the addrList

    //This gives error: best overloaded method match has some invalid
    //arguments
    GenericRepository.AddList<Address>(addrList);
}

AddressWrapped属于IWrapped<Address>类型(即它实现了它)和 Address是给AddList方法的类型参数,所以。{ 类型应该排队。我知道这是由于我对C#的了解有限 泛型(熟悉Java泛型),但无法弄清楚这里有什么问题 --- 应该工作。

这可能没什么区别,但这是我的配置:

  • NHibernate 4.x
  • .NET Framework(4.5)

4 个答案:

答案 0 :(得分:5)

这是因为IList<T>缺少类型方差。 (IList<int>不是IList<object>)。

使用IEnumerable<T>,因为它是协变的:

public static void AddList<T>(IEnumerable<IWrapped<T>> entities)
{
    //Do something
}

原因:如果您获得List<AddressWrapper>的实例,则编译器不知道它是否与任何可能的IList<IWrapped<T>>实现兼容。假设另一个实现IWrapped<T>的类。写入List时它不兼容。即使您没有写入AddList中的列表,编译器也只接受兼容类型。 IEnumerable<T>无法编写,因此可以是变体。

与我建议在您自己的界面中使用协方差的问题无关:

public interface IWrapped<out T>

使IWrapped<Thing>IWrapped<SpecificThing>兼容。

MSDN:https://msdn.microsoft.com/en-us/library/ee207183.aspx

答案 1 :(得分:1)

通过一个例子来说明这一点。如果我们有两种类型的工具IWrapped<T>

,您会期望会发生吗?
public class AddressWrapper : IWrapped<Address>
{
    private Address _wrapped;

    public Address GetWrapped()
    {
        return _wrapped;
    }

    //And some more
}

public class OtherWrapper : IWrapped<MailBox>
{
    public MailBox GetWrapped()
    {
        throw new MailBox();
    }
}

我们尝试将它们添加到AddList<T>内的第三个列表中:

public static void AddList<T>(IList<IWrapped<T>> entities)
{
   internalList = new List<IWrapped<T>>(); 
   list.AddRange(entities); // BOOM.
}

类型系统可以防止你犯错误。由于这个原因,List<T>不完全是协变的。

答案 2 :(得分:0)

在您尝试调用 AddList()时,对于所有编译器都知道,该方法可能会添加任何类型的对象将IWrapper<Address>(即不是AddressWrapper的类型)实现到传入列表中。

这很糟糕,因为您尝试传递给该方法的列表并不想包含AddressWrapper以外的任何内容。

答案 3 :(得分:0)

NB:请参阅@StefanSteinegger的答案,这特别有启发性。

对我有用的是改变我定义addrList的方式,来自:

IList<AddressWrapper> addrList = new List<AddressWrapper>();

为:

IList<IWrapped<Address>> addrList = new List<IWrapped<Address>>();

但是,我也在更改GenericRepository.AddList<T>(..)方法的签名以获取IEnumerable,因为这也有助于指示输入是只读的。所以:

public static void AddList<T>(IEnumerable<IWrapped<T>> entities)
{
    //Do some stuff
}
相关问题