繁体   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