tags:

views:

101

answers:

2

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

How about a cast:

public void Register(TKey key, TComponent component)
{
    component.RegsiterWith(key, (TComponent)this);
}
spender
+7  A: 

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>

i.e. 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!).

Jon Skeet
I don't get it, why should it matter whether or not TComponent could actually be some other implementation of IComponent<TKey, TComponent> as long as it implements "IComponent<TKey, TComponent>" it satisfies the constraint, shouldn't that in itself be sufficient for the compiler?
Abhijeet Patel
No - because "this" might not be a `TComponent`, but the second parameter of `Register`/`RegisterWith` *is* a `TComponent` in the original code. So it's got to convert "`this`" to `TComponent`... but "`this`" might not *be* a `TComponent`.
Jon Skeet
When you say "might not be", I don't see how that would be, given that the class for "this" IS A IComponent<TKey,IComponent>. In what scenario would this not be a TComponent. A concrete example may be helpful in understanding this.
Abhijeet Patel
I've edited my answer to give an example.
Jon Skeet
Ahh... I see the problem now., that was tricky. Thx for explaining this.
Abhijeet Patel