circular generic type parameters

妖精的绣舞 提交于 2019-11-27 14:36:39

问题


I have 2 generic classes, a BaseComponent Class, and a BaseManager class.

They're both abstract and are intended to be made concrete.

public abstract class BaseManager<T> where T : BaseComponent<?>
public abstract class BaseComponent<T> where T : BaseManager<?>

BaseManager has a list of BaseComponents, which is why i want to make it generic, so a PhysicsManager : BaseManager<PhysicsComponent> would have a list of PhysicsComponents.

I want (or rather, think i need) BaseComponent to be generic because i only ever want classes derived from BaseComponent to be 'attached' to their appropriate manager. Ideally i don't want to have to write a constructor per derived component just so i can add it to a passed in concrete manager class. Ideally i want to have a constructor that takes the abstract BaseManager class.

How can i manage this kind of circular dependency?


回答1:


It sounds like you might want to have two generic type parameters:

public abstract class BaseManager<TComponent, TManager>
    where TComponent : BaseComponent<TComponent, TManager>
    where TManager : BaseManager<TComponent, TManager>
public abstract class BaseComponent<TComponent, TManager>
    where TComponent : BaseComponent<TComponent, TManager>
    where TManager : BaseManager<TComponent, TManager>

Yes, it's smelly - but that's the sort of thing I've done in Protocol Buffers.

So then you'd have:

public class PhysicsManager : BaseManager<PhysicsComponent, PhysicsManager>

public class PhysicsComponent : BaseComponent<PhysicsComponent, PhysicsManager>



回答2:


The loosest coupling would be if components didn't know about their managers. Here's an example of how that would work. Note that this approach requires some kind of factory mechanism if all components must be added to a manager. (Nat Pryce - "If a relationship exists between two objects, some other object should establish the relationship.")

abstract class BaseComponent
{
    public event EventHandler SomethingHappened;
}

abstract class BaseManager<TComponent> where TComponent : BaseComponent
{
    List<TComponent> components = new List<TComponent>();

    public virtual void AddComponent(TComponent component)
    {
        components.Add(component);
        component.SomethingHappened += (s, e) => OnSomethingHappened(component);
    }

    public abstract void OnSomethingHappened(TComponent component);
}

If components cannot be independent of their managers, I think it would be better that they depend on an interface defined by the need of the component. This is the Interface Segregation Principle

interface IManager
{
    void ManageMe(BaseComponent component);
}

abstract class BaseComponent
{
    public BaseComponent(IManager manager)
    {
        manager.ManageMe(this);
    }
}

abstract class BaseManager<TComponent> : IManager where TComponent : BaseComponent
{
    void IManager.ManageMe(BaseComponent component)
    {
        ManageMe((TComponent)component);
    }

    protected abstract void ManageMe(TComponent component);
}

interface IPhysicsManager : IManager
{
    void AnotherCallback(PhysicsComponent comp);
}

abstract class PhysicsComponent : BaseComponent
{
    public PhysicsComponent(IPhysicsManager manager)
        : base(manager)
    {
        manager.AnotherCallback(this);
    }
}

abstract class PhysicsManager : BaseManager<PhysicsComponent>, IPhysicsManager
{
    protected override void ManageMe(PhysicsComponent component)
    {
        throw new NotImplementedException();
    }

    public void AnotherCallback(PhysicsComponent comp)
    {
        throw new NotImplementedException();
    }
}

The downside is that the type system doesn't enforce that the correct manager is passed in, and the cast in BaseManager would then fail. I would still prefer this way and "keep the smelliness in my infrastructure" rather than have circular templates polluting all my concrete components and managers.



来源:https://stackoverflow.com/questions/8138839/circular-generic-type-parameters

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!