我的变通量通用参数的解决方法是否必要?

问题描述 投票:2回答:1

我从一个简单的通用界面开始:

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#编译器无法在我的原始单一协变通用接口内部为我完成此技巧?

c# generics covariance contravariance generic-variance
1个回答
0
投票

因为方法使用的TObject是从接口继承的。因此,当构造通用接口类型时,TObject成为该方法的固定类型。

例如,如果接口构造为IFoo<Chrome>,则DoSomething将期望传入Expression<Func<Chrome, TValue>>

将接口转换为IFoo<Browser>不会更改上面构造的方法。并且不允许将Browser的实例传递给期望Chrome的方法,因此这就是编译器引发错误的原因。

但是,如果将TObject的定义移到方法中,则直到调用该方法之前,都不会构造它。因此,编译器不会发现任何明显的问题。如果您的方法实现期望在其他浏览器上使用Chrome,则将获得运行时异常。

尽管语法看起来相同,但并不复杂。

© www.soinside.com 2019 - 2024. All rights reserved.