簡體   English   中英

從另一個泛型類添加泛型方法約束

[英]Adding a generic method constraint from the another generic class

我不確定標題是否反映了我的意思,但......
假設我有兩個類, EntityComponent

public abstract class Entity
{
    private List<Component> _components = new List<Component>();

    public void AddComponent<T>()
        where T : Component
    {
        T component = (T)Activator.CreateInstance(typeof(T));
        component.Owner = this;

        _components.Add(component);
    }
}

public abstract class Component
{
    public Entity Owner { get; protected set; }

    public abstract void Update();
}

正如您可能注意到的,上面的類是abstract classes ,意思是不打算直接使用。 但是,在開發的后期階段,我知道某些Component需要的功能只能由繼承到Entity類的特定類附加/添加。

所以,我添加了一個繼承Component Component<T>類:

public abstract class Entity
{
    private List<Component> _components = new List<Component>();

    public void AddComponent<T>()
        where T : Component
    {
        T component = (T)Activator.CreateInstance(typeof(T));
        component.Owner = this;

        _components.Add(component);
    }
}

public abstract class Component
{
    public Entity Owner { get; protected set; }

    public abstract void Update();
}

public abstract class Component<T> : Component
{
    // I hide the base.Owner with new keyword
    // feel free to suggest me in case there is better approach to do this
    new public T Owner 
    { 
        get { return (T)base.Owner; } 
        protected set { base.Owner = value; }
    }
}

現在,假設我有FooBarProcessor類:

public class Foo : Entity
{
    public int FooValue { get; set; }
}

public class Bar : Entity
{
    public int BarValue { get; set; }
}

public class Processor : Component<Foo>
{
    public override void Update()
    {
        Owner.FooValue = 10;
    }
}

我想要做的是使得只有Foo對象可以添加Processor類。 目前AddComponent忽略它,所以我不知道該怎么做:

var foo = new Foo();
var bar = new Bar();

foo.AddComponent<Processor>(); // OK
bar.AddComponent<Processor>(); // Compiler should give an error at this point

我也試過這樣做:

public void AddComponent<T, X>()
    where T : Component<X>
    where X : Entity
{
    T component = (T)Activator.CreateInstance(typeof(T));
    component.Owner = this;

    _components.Add(component);
}

但是,它需要我明確指定X約束:

foo.AddComponent<Processor, Foo>();
bar.AddComponent<Processor, Bar>(); // Error, but the syntax is weird!

有任何想法嗎?

您的帖子不清楚您對基本EntityComponent類的約束(如果有)。 所以我不知道你的場景下面是否可行。 也就是說,我相信如果不是,你將無法做你想做的事情,否則編譯器就不會知道泛型類型參數。

沒有任何其他約束的解決方案是使您的Entity類通用,並提供子類類型本身作為類型參數:

class Entity { }

class Entity<T> : Entity where T : Entity<T>
{
    public void AddComponent<U>(U value) where U : Component<T> { }
}

class Component<T> where T : Entity { }

class Foo : Entity<Foo> { }

class Bar : Entity<Bar> { }

class P : Component<Foo> { }

我知道它看起來很奇怪。 但是你基本上要求一個通用類型依賴的自引用圖,而在C#代碼中,上面就是這樣。

您可以使用類型推斷調用AddComponent()方法(因此不需要通用參數)。 如果您嘗試使用錯誤類型的Component<T>對象調用它,您將收到編譯器錯誤:

Foo foo = new Foo();
Bar bar = new Bar();
P p = new P();

foo.AddComponent(p);
bar.AddComponent(p); // CS0311


注意:我強烈建議不要隱藏班級成員。 它並沒有真正影響你提出的問題(即你可以完全拋棄那個細節),但是有兩個不同的屬性同名只是要求bug。 如果你必須使用隱藏,恕我直言你應該至少讓新屬性使用隱藏屬性。 例如:

class Component
{
    public Entity Owner { get; protected set; }
}

class Component<T> : Component where T : Entity
{
    new public T Owner
    {
        get { return (T)base.Owner; }
        set { base.Owner = value; }
    }
}

您不會對非泛型Component.Owner屬性的賦值進行編譯時檢查,但是如果某些代碼嘗試將Owner屬性作為通用版本取消引用,則至少會出現運行時錯誤,如果和何時由於某種原因,基類型分配了錯誤的類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM