Attila Bicskó
Attila Bicskó

Reputation: 80

Inheriting generic abstract

I'm not sure if this is possible at all, looking for some clarification.

I have a class structure like this:

public class FooBase
{
    //Some base class
}

public class BarBase
{
    //Some base class    
}

public class Foo : FooBase
{
    //Implementation
}

public class Bar : BarBase
{
    //Implementation
}

public abstract class FooBarHolderAbstract<T, V> where T: FooBase where V: BarBase
{
}

public class MyFooBarHolderImpl : FooBarHolderAbstract<Foo, Bar>
{
}

public class FooBarTest
{
    public void DoSomethingWithFooBar<T>() where T : FooBarHolderAbstract<FooBase, BarBase>
    {
        //Do something tith the obj
    }

    public void RunTest()
    {
        //This doesn't work, compiler says MyFooBarHolder is not convertible to FooBarHolderAbstract<FooBase, BarBase>
        DoSomethingWithFooBar<MyFooBarHolderImpl>();
    }
}

In the FooBarTest class, I'd like to create a method which accepts a generic parameter, which inherits from the abstract class having two generic parameters. The class MyFooBarHolderImpl extends the abstract base class and specifies its generic parameters with types which are inheriting from the abstract class' generic parameter types.

When I try to call this method (DoSomethingWithFooBar()) the compiler tells me that the type MyFooBarHolderImpl must be convertible to FooBarHolderAbstract

Is this something which cannot be done at all, or am I missing a concept/syntax?

Thanks in advance!

Upvotes: 1

Views: 113

Answers (3)

Dennis
Dennis

Reputation: 37800

It doesn't clear, what are you going to do in DoSomethingWithFooBar, since you don't pass any parameter, but here are another options:

public class FooBarTest
{
    public void DoSomethingWithFooBar<TFooBase, TBarBase>(FooBarHolderAbstract<TFooBase, TBarBase> obj) 
        where TFooBase : FooBase
        where TBarBase : BarBase
    {
        //Do something tith the obj
    }

    public void RunTest()
    {
        DoSomethingWithFooBar<Foo, Bar>(new MyFooBarHolderImpl());
    }
}

or

public class FooBarTest
{
    public void DoSomethingWithFooBar<TFooBase, TBarBase, THolder>() 
        where TFooBase : FooBase
        where TBarBase : BarBase
        where THolder : FooBarHolderAbstract<TFooBase, TBarBase>
    {
        //Do something tith the obj
    }

    public void RunTest()
    {
        DoSomethingWithFooBar<Foo, Bar, MyFooBarHolderImpl>();
    }
}

Upvotes: 1

Gagan Jaura
Gagan Jaura

Reputation: 719

You have to write your FooBarTest as below. You have to define T for DoSomethingWithFooBar<T> as FooBarHolderAbstract<Foo, Bar>

    public class FooBarTest
    {
        public void DoSomethingWithFooBar<T>() where T : FooBarHolderAbstract<Foo, Bar>
        {
            //Do something tith the obj
        }

        public void RunTest()
        {                         
            DoSomethingWithFooBar<MyFooBarHolderImpl>();
        }
    }

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1504172

Well, it can't be done directly - a FooBarHolderAbstract<Foo, Bar> isn't a FooBarHolderAbstract<FooBase, BarBase>. It's not clear whether or not you could logically have that, because we don't know what's in the abstract class.

You're basically looking for generic covariance, but that isn't supported on classes anyway - so you may want to introduce an interface:

public interface IFooBarHolder<out T, out V>
    where T: FooBase
    where V: BarBase
{
    // Define what you need in here
}

public abstract class FooBarHolderAbstract<T, V> : IFooBarHolder<T, V>
    where T : FooBase
    where V : BarBase
{

}

At that point, you can change FooBarTest to:

public void DoSomethingWithFooBar<T>() where T : IFooBarHolder<FooBase, BarBase>
{
    //Do something with the obj
}

... because an IFooBarHolder<Foo, Bar> is an IFooBarHolder<FooBase, BarBase>.

However, this only works if you can define all your operations for the interface which use T and V in "out" positions, e.g. return types from methods. If you ever need them in "input" positions, e.g. as method parameters, you're stuck - because a method expecting a Foo can't handle any other kind of FooBase.

Upvotes: 2

Related Questions