是否可以分配像Generic <Base>这样的泛型?generic = new Generic <SubClass>();

时间:2019-09-18 13:05:51

标签: c# generics mapping

我希望能够声明一个以基类为类型参数的泛型实例,然后再将其分配给以子类为参数的泛型。我已经可以在Java中执行类似的操作,但是在C#中却停留在这里。

此类问题的最佳用例是将不同的交互映射到两个类类型,这是我在此代码片段中所做的。我试图声明一个基类以及从中派生的类A和B。然后,我想映射一个将两个类作为参数的函数。

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());
    }
}

编译器希望使用通用(Func),如doStuffWithAandB(Base a,Base b),而不是doStuffWithAandB(A a,B b)。如果C#不允许这样的功能。为什么会这样,并且是解决此类问题的更好解决方案?

2 个答案:

答案 0 :(得分:2)

在C#中,只能将接口和委托指定为变量。因此,如果您定义了IGeneric<T>接口,则可以将其关联到Generic<SubClass>

interface IGeneric<out T> { }

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

class Base { }

class SubClass : Base { }

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

在C#8.0+中,接口可能包含默认实现:https://devblogs.microsoft.com/dotnet/default-implementations-in-interfaces/

您不能将Func<Base, Base, bool>Func<Derived, SubClass , bool>关联,因为in参数类型T1T2是互变的:

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

有关此的更多信息,请参阅@Eric Lippert的答案here

答案 1 :(得分:0)

想象一下,这将按照您的想法工作。某些API的委托期望Base的实例。该API肯定会在某个时刻使用Base的任意实例来调用该委托,例如B

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

因此,通过将Func<A, bool>分配给类型Func<Base, bool>的参数,该代码现在将为您期望的B提供A

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

以上内容在运行时几乎不会崩溃,因为B无法转换为A

另一方面,您可以将Func<Base, bool>分配给Func<A, bool>

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

您可能会这样称呼:

DoSomething(DoStuffWithBase);

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

之所以可行,是因为要传递给委托的实例的类型为A,它继承了Base

这就是我们所说的Contra-variance

顺便提一句:虽然参数对于委托人是反变量,但其 return-value 是协变量,这意味着您可以返回<您的代表提出的strong>更多具体要求,因为每个Specific已经是Generic