C# 类型强制转换为泛型接口

本文关键字:泛型接口 转换 类型 | 更新日期: 2023-09-27 18:31:03

我正在编写一个将一堆对象呈现到屏幕的库。子对象是抽象的,它旨在让此库的用户从此抽象类派生自己的子对象。

public abstract class Child : IRenderable {}
public interface IParent<T> where T : Child
{
   IEnumerable<T> Children { get; }
}

复杂的是我没有可以使用的IParent列表,相反,我有一堆IRenderables。库的用户应该写这样的东西:

public class Car : IRenderable { }
public class Cow : IRenderable, IParent<Calf> { }
public class Calf : Child { }
// note this is just an example to get the idea
public static class App
{
   public static void main()
   {
      MyLibraryNameSpace.App app = new MyLibraryNameSpace.App();
      app.AddRenderable(new Car()); // app holds a list of IRenderables
      app.AddRenderable(new Cow());
      app.Draw(); // app draws the IRenderables
   }
}

在 Draw() 中,库应该强制转换并检查 IRenderable 是否也是 IParent。但是,由于我不了解小牛,我不知道该把牛变成什么。

// In Draw()
foreach(var renderable in Renderables)
{
   if((parent = renderable as IParent<???>) != null) // what to do?
   {
      foreach(var child in parent.Children)
      {
          // do something to child here.
      }
   }
}

我怎样才能克服这个问题?这与协方差泛型或其他什么有关(我不熟悉协方差概念)?

C# 类型强制转换为泛型接口

由于 IParent<T>返回类型 T 的项目,您可以使用 out 修饰符使其协变:

public interface IParent<out T> where T : Child
{
   IEnumerable<T> Children { get; }
}

这将使IParent<anything>可转换为IParent<Child>

IParent<Child> parent = renderable as IParent<Child>; // works for Cow

请注意,协方差仅在您只返回类型为 T 的对象时才有效(简单地说)。例如,一旦将 AddChild(T) 方法添加到 IParent 接口,协方差必须中断(= 编译器将抱怨),因为否则,可能会编写以下类型不安全的代码:

IParent<Child> parent = renderable as IParent<Child>;
parent.AddChild(new Kitten()); // can't work if parent is really a Cow.

你可以实现中间非通用接口IParent:

public interface IParent
{
    IEnumerable<Child> Children { get; }
}
public interface IParent<T> : IParent
  where T: Child
{
    IEnumerable<T> Children { get; }
}

然后在函数中强制转换为 IParet。

类似以下内容?

static void draw(List<IRenderable> renderables)
{
    foreach (IRenderable render in renderables)
    {
        if (render is IParent<Child>)
        {
            foreach (Child c in ((IParent<Child>)render).Children)
            {
                //do something with C?
            }
        } 
    }
}