从另一个泛型类添加泛型方法约束

时间:2016-03-13 12:57:51

标签: c# .net generics

我不确定标题是否反映了我的意思,但是...... 我们假设我有两个班级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<T>的小组Component

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;
    }
}

我想要做的是使Processor类仅由Foo对象添加。目前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!

有什么想法吗?

1 个答案:

答案 0 :(得分:0)

您的帖子并不清楚您对基本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


注意:我强烈建议不要隐藏班级成员。它并没有像你所说的那样真正影响你的问题(也就是你可以完全抛弃那个细节),但是拥有两个不同的同名属性只是在寻找错误。如果你必须使用隐藏,恕我直言你应该至少让新属性使用隐藏属性。 E.g:

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属性的分配进行编译时检查,但如果某些代码尝试取消引用{{},则至少会遇到运行时错误{1}}属性作为通用版本,如果由于某种原因由基类型分配了错误的类型。