泛型继承和转换

时间:2016-02-05 15:29:25

标签: c# generics inheritance

我有以下课程:

class Item { }
class MeetingItem : Item { }
class ItemGroup<T> { }

现在,这没有问题:

Item something;
something = new MeetingItem();

然而失败了:

ItemGroup<Item> itemGroup;
itemGroup = new ItemGroup<MeetingItem>(); // Fails here

我收到“无法隐式转换类型'ItemGroup<MeetingItem>'到'ItemGroup<Item>'”错误。这不正是我正在做的事情(将类型分配给Item,然后实例化到MeetingItem)?

我最终将拥有ItemGroup类中的Items集合以及其他一些成员。然后我将会有一个包含不同类型派生项的ItemGroups集合。理想情况下,我希望将Item作为一个抽象类(可能是一个接口,我可能需要根据我需要实现的内容保持一个类)。

我对任何重构的想法持开放态度。

感谢。

编辑以在没有Generic ItemGroup的情况下添加我的解决方案: 我决定放弃仿制药......有点......我拼凑了这个:

public class ItemGroup {
  public Type ItemType => this.Items.GetType().GetGenericArguments()[0];
  public IList Items { get; set; }

  public ItemGroup(Type itemType) {
    var genericListType = typeof(List<>).MakeGenericType(itemType);
    Items = (IList)Activator.CreateInstance(genericListType);
  }
}

所以,我现在可以这样做:

List<ItemGroup> groups = new List<ItemGroup>();
groups.Add(new ItemGroup(typeof(MeetingItem));

然后我可以通过以下方式测试特定的Item类型:

groups[0].ItemType == typeof(MeetingItem)

看起来有点hacky,但它确实有效。我对ItemType属性的性能有点担心,我对任何重构的想法持开放态度。

1 个答案:

答案 0 :(得分:2)

这是一个方差问题。

考虑一个这样的简单界面:

interface MyInterface<T>
{
  T GetStuff();
  void SetStuff(T value);
}

现在,您有MyInterface<A>MyInterface<B>,其中B继承自A

返回B代替A始终是安全的。但是,返回A代替B并不安全。因此,查看GetStuff时,应该可以将MyInterface<B>转换为MyInterface<A>,但反之亦然。

通过B代替A始终是安全的。但是,传递A代替B是不安全的。因此,查看SetStuff时,应该可以将MyInterface<A>转换为MyInterface<B>,但反之亦然。

问题应该是显而易见的 - 你不能同时实现这两个目标。 两种方法都没有安全演员。

如果您可以避免在单个界面中同时使用这两种方式,则可以使用out / in关键字来指定您的界面支持哪种差异,但这样做是正确的。查看.NET框架中的类:

IEnumerable<object> enumerable = Enumerable.Empty<string>(); // Safe, 
                                                             // enumerable is covariant
ICollection<object> collection = new Collection<string>();   // Error, 
                                                             // collection isn't covariant

Func<object> func = () => "Hi!"; // Safe, Func<T> is covariant
Action<object> func = (string val) => { ... }; // Error, Action<T> isn't covariant

另一方面,有逆转:

Func<string> func = () => new object(); // Error, Func<T> isn't contravariant
Action<string> func = (object val) => { ... }; // Safe, Action<T> is contravariant