簡體   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