C# 是否需要我的协变通用参数解决方案?
我从一个简单的通用接口开始:C# 是否需要我的协变通用参数解决方案?,c#,generics,covariance,contravariance,generic-variance,C#,Generics,Covariance,Contravariance,Generic Variance,我从一个简单的通用接口开始: interface IFooContext<TObject> { TObject Value { get; } String DoSomething<TValue>( Expression<Func<TObject,TValue>> lambdaExpression ); } // Usage: IFooContext<Panda> ctx = ... String str = ctx.
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 );
因此,对于DoSomething
声明,我得到了这个编译器错误:
错误CS1961无效差异:类型参数“TObject”必须在“IFooContext.DoSomething(Expression)
”上始终有效“对象”是协变的
在将各种想法抛到墙上之后,我发现我可以通过将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 );
接口IFooContext
{
字符串DoSomething(表达式lambdaExpression);
}
接口IFooContext
{
对象值{get;}
}
公共静态类FooContextensions
{
公共静态字符串DoSomething(此IFooContext上下文,表达式lambdaExpression)
{
返回context.DoSomething(lambdaExpression);
}
}
//实际使用情况:
IFooContext ctx1=。。。
IFooContext ctx2=ctx1;//是的,协方差!
字符串str=ctx2.DoSomething(bear=>bear.PoopsInTheWoods);
这在编译和运行时没有任何问题——实际用法的语法与我前面示例的预期用法相同
为什么这会起作用?为什么C编译器不能用我原来的单一协变泛型接口在内部为我做这件事?因为方法使用的
TObject
是从接口继承的。因此,当构造泛型接口类型时,TObject
成为方法的固定类型
例如,如果接口构造为IFoo
,则DoSomething
将期望传入表达式
将接口转换为IFoo
不会改变上述构造方法。不允许将Browser
的实例传递给需要Chrome
的方法,因此编译器会抛出错误
但是,如果将TObject
的定义移动到方法,则在调用该方法之前不会构造该定义。i、 e.如果您在中通过浏览器
,则该方法将使用浏览器
构建。因此,编译器不会发现明显的问题。如果您的方法实现希望在其他浏览器上使用Chrome
,则会出现运行时异常
尽管语法看起来是一样的,但编译后的代码并没有隐藏起来。@MichaelRandall假设方差约束只适用于泛型接口,而不适用于泛型类(即实现),“契约”对实现者来说是一样的(当实现者本身是开放泛型类型时)-只有与消费者的合同不同。是的,经过大约10秒钟的思考后,我收回了评论,我自己也对技术解释感兴趣
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 );