Generic interfaces and inheritance to call a generic method

Viewed 144

I want to have a generic class which could call a method based on its generic type which is defined by a derived class. For that I implemented a base interface and a generic interface which has the base interface as generic and also derives from the base interface.

In the generic interface I want a method based on the type T of the base interface.

After that I wanted to implement a class based on the generic interface which should be able to call the generic method. This is the example code:

public interface BaseInterface
{ }

public interface GenericInterface<T> : BaseInterface where T : BaseInterface
{
    void Foo(T t);
}

public class C<T> : GenericInterface<T> where T : BaseInterface
{
    public C()
    {
        // None of these works
        Foo(this);
        Foo((T)this);
        Foo((BaseInterface)this);   
    }

    public void Foo(T t) { }
} 

Is there a way to achieve my desired behavior here?

The error message here is:

cannot convert from 'C<T>' to 'T'

which in my eyes should be possible because C derives from BaseInterface which is T

4 Answers

While both C<T> and T need to derive from BaseInterface, that does not mean that then need to be the same. I might for example declare another type B : BaseInterface, and C<B>. So we would get the method Foo(B t) , it would obviously not be possible to call the method with this as the parameter, since C<B> is not B.

If you just need a method that needs a BaseInterface parameter, just declare it as Foo(BaseInterface t) instead. That way you could call it with this without any problem.

It looks like you're looking for something like the Curiously Recurring Template Pattern for C#. In a simplified form it looks like this:

class Base<T>
{
    public void Foo(T t) { }
}

class C : Base<C>
{
    C()
    {
        Foo(this);
    }
}

In your case T is a type that derives from BaseInterface. Although C<T> also derives from BaseInterface it doesn't mean that you can bind a C<T> to T t. It's like trying to bind a string to a List, just because both implement IEnumerable. They are still different types.

For your full example it could look like this

public interface BaseInterface
{ }

public interface GenericInterface<T> : BaseInterface where T : BaseInterface
{
    void Foo(T t);
}

public class C<T> : GenericInterface<C<T>> where T : BaseInterface
{
    public C()
    {
        Foo(this);
    }

    public void Foo(C<T> t) { }
} 

I Tried this, maybe can help you

public class C<T> : GenericInterface<T> where T : BaseInterface
{
    public C()
    {
        T t = default(T);
        BaseInterface bi;
        bi = t; // here can cast , bacuse T : BaseInterface , not BaseInterface : T
        t = bi;//here cast error , 
    }

    public void Foo(T t) { }
}

It's same like this case:

public class B { }
public class A : B
{
    public void CastTest()
    {
        A a = null;
        B b = null;
        b = a;
        a = b;//here error
    }
}

Here's what you need for the Curiously Recurring Template in C#.

public interface BaseInterface { }
public interface GenericInterface<T> : BaseInterface where T : GenericInterface<T>
{
    void Foo(T t);
}

public abstract class C<T> : GenericInterface<T> where T : C<T>
{
    public abstract void Foo(T t);
}

Now you can go ahead and implement a real class:

public class D : C<D>
{
    public D()
    {
        Foo(this);
        Foo((D)this);
    }

    public override void Foo(D t) { }
}

That works fine.

However, calling Foo((BaseInterface)this); will never work in this code. It just doesn't make sense.

Related