泛型继承和转换

本文关键字:转换 继承 泛型 | 更新日期: 2023-09-27 18:33:20

我有以下类:

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>'"错误。 这不正是我上面正在做的事情(将某些内容分配给类型项目,然后实例化到会议项目(?

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

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

谢谢。

编辑以添加我的解决方案,而无需通用项组:我决定放弃泛型...有点... 我拼凑了这个:

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));

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

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

这似乎有点笨拙,但它有效。 我有点担心 ItemType 属性的性能,并且我对任何重构想法都持开放态度。

泛型继承和转换

这是一个方差问题。

考虑一个简单的界面,如下所示:

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