1

I want to be able to declare a generic instance that takes a base class as a type parameter and then later assign it to a generic that takes the subclass as the parameter. I have been able to do similar things in Java, but I'm stuck here in C#.

The best use case for a problem like this would be to map different interactions to two class types, which I have done in this code snippet. I tried to declare a Base class and classes A and B that derive from it. Then I want to map a function which takes the two classes as parameters.

class Program
{

    public class Base {}
    public class A : Base {}
    public class B : Base {}

    public class TypePair
    {
        private Type left;
        private Type right;

        public TypePair(Type left, Type right)
        {
            this.left = left;
            this.right = right;
        }
    }

    public static Dictionary<TypePair, Func<Base, Base, bool>> typeMap = new Dictionary<TypePair, Func<Base, Base, bool>>();

    public static bool doStuffWithAandB(A a, B b)
    {
        Console.WriteLine("Did stuff with a and b!");
        return true;
    }

    public static void Main(string[] args)
    {
        var typePair = new TypePair(typeof(A), typeof(B));
        typeMap.Add(typePair, doStuffWithAandB); // <- Compiler error :(
        typeMap[typePair](new A(), new B());
    }
}

The compiler expects a generic (Func) like doStuffWithAandB(Base a, Base b) and not doStuffWithAandB(A a, B b). If C# does not allow such a functionality. Why is that and are the better solutions to problems like these?

2 Answers2

3

Only interfaces and delegates can be specified as variant in C#. So if you define an IGeneric<T> interface, you can assing it to a Generic<SubClass>:

interface IGeneric<out T> { }

class Generic<T> : IGeneric<T> { }

class Base { }

class SubClass : Base { }

IGeneric<Base> obj = new Generic<SubClass>();

In C# 8.0+, interfaces may contain default implementations: https://devblogs.microsoft.com/dotnet/default-implementations-in-interfaces/

You can't assing a Func<Base, Base, bool> to a Func<Derived, SubClass , bool> because the in parameter types T1 and T2 are contravariant:

public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2)

Please refer to @Eric Lippert's answer here for more information about this.

mm8
  • 163,881
  • 10
  • 57
  • 88
0

Imagine this would work as you think. Some API has a delegate that expects an instance of Base. Surely this API will call that delegate at some point with an arbitrary instance of Base, e.g. a B:

void DoSomething(Func<Base, bool> func)
{
    Base b = new B();
    func(b);
}

So by assigning a Func<A, bool> to a parameter of type Func<Base, bool>, that code would now provide a B for what you expect to be an A:

bool DoStuffWithA(A a)
{
    Console.WriteLine(a.SpecialAStuff);
}

The above would crash hardly at runtime, as B is not convertible to A.

On the other hand you can assign a Func<Base, bool> to a Func<A, bool>:

DoSomething(Func<A, bool> func)
{
    Base a = new A();
    func(a);
}

which you may call like this:

DoSomething(DoStuffWithBase);

bool DoStuffWithBase(Base b)
{
    Console.WriteLine(b.BaseStuff);
}

This works because the instance being passed to the delegate is type A, which inherits Base.

That´s what we call Contra-variance.

Just an asside: while the parameters are contra-variant for a delegate, its return-value is co-variant, which means you can return anything that is more specific from your delegate, as every Specific already is a Generic.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111