繁体   English   中英

具有多态性的C#泛型

[英]C# generics with polymorphism

你好,堆栈溢出的居民,我遇到一个关于多态性和泛型的问题(也许理解?)。 我希望能够定义一个包含“组件”的“系统”。 我还希望能够扩展系统和组件以具有更大的功能。

我目前有两个基类,Component和I / A_ComponentSystem(为简洁起见,我将不显示任何实际代码,仅显示定义):

public abstract class Component { }

public interface IComponentSystem { }
public interface IComponentSystem<TComponent> : IComponentSystem
    where TComponent : Component { }

// The following are what should should actually be inherited from
public abstract class AComponentSystem : IComponentSystem { }
public abstract class AComponentSystem<TComponent> : AComponentSystem
    where TComponent : Component { }

以下是创建的示例组件/系统:

public abstract class ITag : Component { } // This is to allow generating the code in a different binary. Hard to explain in the question, I'll clarify if need be
public class Tag : ITag { }

public abstract class ITagSystem : AComponentSystem<ITag> { }
public class TagSystem : ITagSystem { }

以下是实际尝试在不同对象之间使用代码/变形的一些摘录(请注意,代码并非旨在以这种方式使用,但是我正在编写单元测试以确保层之间的兼容性)

// This is the main system that will be passed around
TagSystem tagSys = new TagSystem();

// This is OK
ITagSystem ITagSys = (ITagSystem)ITagSys;

// This is OK
AComponentSystem A_TagSys = (AComponentSystem)tagSys;

// This is OK
AComponentSystem<ITag> ATag_TagSys = (AComponentSystem<ITag>)tagSys;

// This is OK
IComponentSystem I_TagSys = (IComponentSystem)tagSys;

// This is OK
IComponentSystem<ITag> ITag_TagSys = (IComponentSystem<ITag>)tagSys;

// Even the following is OK (which is why I am confused)
IComponentSystem<Tag> new_ITag_TagSys = (IComponentSystem<Tag>)tagSys;

//***This is where it blows up*** (1)
AComponentSystem<Tag> new_ATag_TagSys = (AComponentSystem<Tag>)tagSys;

我还有另一个接口/类SystemManager,它的定义如下:

public interface ISystemManager
{
    TComponent AddNewComponentToEntity<TComponent, TComponentSystem>(Entity e) // Please don't ask about Entity, it shouldn't be required for this snippet and I already feel like I've posted a lot)
        where TComponent : Component, new() // Required for some reason or I get an error
        where TComponentSystem : IComponentSystem<TComponent>;
}

现在,我在这里拥有的特定代码块也将引发错误:

//*** blows up here as well ***(2)
ISystemManager sysMan = new SystemManager(); // defined elsewhere
sysMan.AddNewComponentToEntity<Tag, ITagSystem>(entity);

就我收到的错误而言,错误(1)是:

Cannot convert type 'TagSystem' to 'AComponentSystem<Tag>'

错误(2)如下:

The type 'ITagSystem' cannot be used as type parameter 'TComponentSystem' in the generic type or method 'ISystemManager.AddNewComponentToEntity<TComponent,TComponentSystem>(Entity)'. There is no implicit reference conversion from 'ITagSystem' to 'IComponentSystem<Tag>'.

现在,就我的问题而言,因此是:

  1. 为什么不能将TagSystem转换为AComponentSystem<Tag> 这似乎是有效的变体。
  2. 为什么ITagSystem无法转换为IComponentSystem<Tag> 看来Tag仍应符合受支持的ITag。
  3. 有什么办法可以在保留很多抽象层的同时更改层次结构?

感谢您阅读本文并为我提供帮助。

注意:是的,这是用于EntityFramework驱动的游戏引擎。 我主要将其构建为自己的练习,因此我可以为自己快速启动3d项目。 是的,我之前已经建立了一些游戏项目,不,我对“完成”游戏不感兴趣,我只是在修补和玩耍。

如果没有一个更简单但更完整的代码示例,就不可能在您的特定情况下提供特定的建议。 但是,基本问题是类型确实不可转换,正如编译器所说的那样。

  1. 为什么不能将TagSystem转换为AComponentSystem<Tag> 这似乎是有效的变体。

TagSystem不继承AComponentSystem<Tag> 它继承了AComponentSystem<ITag> 这两种类型实际上并不相同。 仅因为Tag继承/实现ITag ,并不意味着AComponentSystem<Tag>自动继承/实现AComponentSystem<ITag> 如果确实如此,则意味着通常会返回Tag类型值的AComponentSystem<Tag>的方法或属性可能会在期望Tag值的情况下使用,但ITag某些其他实现是真的回来了。 这是因为您将能够转换为AComponentSystem<Tag> ,然后使用该引用将ITag的非Tag实现返回到一些只需要Tag

由于我希望是显而易见的原因,因此这很糟糕,因此编译器不允许您这样做。

  1. 为什么ITagSystem无法转换为IComponentSystem<Tag> 看来Tag仍应符合受支持的ITag。

没有良好的最小,完整和可验证的代码示例 ,很难回答问题的这一部分,因为所显示的类型与所显示的代码不一致。 ITagSystem被声明为继承AComponentSystem<ITag> ,后者继而仅实现IComponentSystem ,而不实现IComponentSystem<TComponent>

因此,根据显示的代码,您甚至没有天真的认为转换可以正常进行。 但是让我们假设一下,您所显示的类型声明中有一个错字。 然后答案基本上与上述相同:实现IComponentSystem<ITag>与实现IComponentSystem<Tag>

  1. 有什么办法可以在保留很多抽象层的同时更改层次结构?

可能吧。 这取决于这些类型的实际作用。 从C#4开始,我们已经能够在具有协方差和相反方差的接口上指定泛型类型参数。 通过限制类型参数并匹配接口成员,接口可以支持特定的转换方案,就像您要尝试的那样。

但是请注意,这仅在接口成员确实与此类转换兼容时才有效。 编译器仍然不会让您做任何不安全的事情。

关于Stack Overflow的讨论已经有很多问题。 从技术上讲,您的问题甚至可以被认为是这些问题的重复。 希望以上内容能解决您的紧迫问题,并为您提供足够的信息以进行更多研究,并查看通用接口差异是否适合您的情况。 如果您需要更多帮助,建议您发布一个新问题,并确保包括一个良好的MCVE,该MCVE可以以最简单的方式清楚地说明您的问题。

  1. TagSystem遥远地继承了AComponentSystem<ITag> ,但是您试图将其转换为AComponentSystem<Tag> (请注意,通用类型中没有“ I”。) AComponentSystem<>这两种通用类型完全不同,因此您不能在两者之间自由转换。
  2. 与第1点相同,仅因为TagITag并不意味着IComponentSystem<Tag>IComponentSystem<ITag>
  3. 答案几乎肯定是肯定的,尽管确切的方式完全取决于您将如何使用它。 您可能还想问自己,是否真的需要这么多抽象层。

为了更好地说明我的观点,举一个常见的通用类型为例:List。 如果泛型遵循与普通类相同的继承规则,则List<Car>将是List<Vehicle>的子类型。 但是两者之间的区别在于,第一个列表只能容纳汽车,而第二个列表可以容纳任何车辆 因此,如果这些列表是父母和孩子,则可以执行以下操作:

List<Car> cars = new List<Car>();
List<Vehicle> vehicles = (List<Vehicle>)cars;
vehicles.Add(new Truck());

你看到问题了吗? 继承的一般规则仅允许我们将非Car对象添加到汽车列表之外。 或者,如果不是合法的话,他们会这样做。 实际上, List<Car>List<Vehicle>没有任何关系,但实际上是完全独立的类,没有任何直接关系。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM