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