简体   繁体   中英

Using 'this' as a generic argument, conversion issues

Is this possible? When i compile i get an error saying cannot convert Component to TComponent even with the constraint

public interface IComponent<TKey, TComponent> where TComponent : IComponent<TKey, TComponent>
{
    TComponent Parent { get; }
    void Register(TKey key, TComponent component);
    void RegsiterWith(TKey key, TComponent component);
}

public class Component<TKey, TComponent> : IComponent<TKey, TComponent> where TComponent : IComponent<TKey, TComponent>
{
    private TComponent _parent;

    public void Register(TKey key, TComponent component)
    {
        component.RegsiterWith(key, this);
    }

    public void RegsiterWith(TKey key, TComponent component)
    {
        component.Register(key, this);
    }

    public TComponent Parent { get { return _parent; } }
}

The reason that it fails is because TComponent could actually be some other implementation of IComponent<TKey, TComponent> . Just because Component implements the interface doesn't mean that nothing else can :)

One way round this would be to change the interface:

public interface IComponent<TKey, TComponent> 
    where TComponent : IComponent<TKey, TComponent>
{
    TComponent Parent { get; }
    void Register(TKey key, IComponent<TKey, TComponent> component);
    void RegsiterWith(TKey key, IComponent<TKey, TComponent> component);
}

Whether that's feasible in your situation I don't know, but it would certainly avoid the type issue.

Another option would be to cast this as spender suggested. This could fail at execution time, but in reality unless you do create an alternative implementation, it won't. It's slightly annoying, having to cast when using generics, but occasionally it happens.

EDIT: Here's an example of how it could go wrong:

public class Other : IComponent<string, Other>
{
    // Implement interface
}

Now what happens if you create a Component<string, Other> ? It satisfies the constraint with no problems... but a Component isn't an Other ...

Now you could change your constraint like this:

public class Component<TKey, TComponent> : IComponent<TKey, TComponent> 
    where TComponent : Component<TKey, TComponent>

ie constrain TComponent on Component instead of IComponent . That still has issues though:

public class FooComponent : Component<string, FooComponent> {}
public class BarComponent : Component<string, FooComponent> {}

Here, you probably want BarComponent to have a TComponent of itself, but it's got a different component. The code you've shown so far will probably be okay, but this sort of quirk may hamper other goals. Worth considering...

Oh, and if you're happy to make Component sealed, at that point the changed constraint is enough (I think!).

How about a cast:

public void Register(TKey key, TComponent component)
{
    component.RegsiterWith(key, (TComponent)this);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM