我有一个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();
}
我有以下用于保存这些实体的通用类(还有其他类
遵循此Entity
和EntityWrapper
)模式的实体:
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泛型),但无法弄清楚这里有什么问题
--- 应该工作。
这可能没什么区别,但这是我的配置:
答案 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>
兼容。
答案 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
}