我从一个简单的通用接口开始:
interface IFooContext<TObject>
{
TObject Value { get; }
String DoSomething<TValue>( Expression<Func<TObject,TValue>> lambdaExpression );
}
// Usage:
IFooContext<Panda> ctx = ...
String str = ctx.DoSomething( panda => panda.EatsShootsAndLeaves );
但是我需要使这个接口的泛型类型协变(出于我不会讨论的原因),但这会导致编译器错误,因为Func<T0,TReturn>
需要T0
逆变(in T0
)或不变参数:
interface IFooContext<out TObject>
{
TObject Value { get; }
String DoSomething<TValue>( Expression<Func<TObject,TValue>> lambdaExpression );
}
// Intended usage:
IFooContext<Panda> ctx1 = ...
IFooContext<Ursidae> ctx2 = ctx1; // yay for covariance!
String str = ctx2.DoSomething( bear => bear.PoopsInTheWoods );
所以我得到这个DoSomething
声明的编译器错误:
错误 CS1961 变化无效:类型参数“TObject”必须在“
IFooContext<TObject>.DoSomething<TValue>(Expression<Func<TObject, TValue>>)
”上始终有效。'TObject' 是协变的。
在向墙上抛出各种想法后,我发现我可以通过移动DoSomething
到非泛型接口并 TObject
在方法上指定其参数来解决此问题,然后将最初预期的方法“公开”为扩展方法,如下所示:
interface IFooContext
{
String DoSomething<TObject,TValue>( Expression<Func<TObject,TValue>> lambdaExpression );
}
interface IFooContext<TObject>
{
TObject Value { get; }
}
public static class FooContextExtensions
{
public static String DoSomething<TObject,TValue>( this IFooContext<TObject> context, Expression<Func<TObject,TValue>> lambdaExpression )
{
return context.DoSomething<TObject,Value>( lambdaExpression );
}
}
// Actual usage:
IFooContext<Panda> ctx1 = ...
IFooContext<Ursidae> ctx2 = ctx1; // yay for covariance!
String str = ctx2.DoSomething( bear => bear.PoopsInTheWoods );
这编译和运行没有任何问题 - 实际用法的语法与我之前示例的预期用法相同。
为什么这行得通,为什么 C# 编译器不能用我原来的单一协变泛型接口在内部为我做这个技巧?