我希望能够声明一个以基类为类型参数的泛型实例,然后再将其分配给以子类为参数的泛型。我已经可以在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#不允许这样的功能。为什么会这样,并且是解决此类问题的更好解决方案?
答案 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参数类型T1
和T2
是互变的:
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
。