【问题标题】:Co/contravariance with Func<in T1, out TResult> as parameter以 Func<in T1, out TResult> 作为参数的协/逆变
【发布时间】:2016-05-30 13:47:04
【问题描述】:

假设我有一个界面,例如

public interface IInterface<in TIn, out TOut> {
  IInterface<TIn, TOut> DoSomething(TIn input);
}

TIncontra-变体,TOutco-变体。

现在,我希望调用者能够指定要在输入值上执行的某个函数,所以我会天真地将以下方法添加到接口中:

IInterface<TIn, TOut> DoSomethingWithFunc(Func<TIn, TOut> func);

哪个……不起作用。 TIn 现在需要是协变的,而TOut 是逆变的。

我明白,我不能使用协变泛型类型作为方法的输入,但我认为我可以在嵌套泛型类型中使用它们,该类型本身指定方差 (Func&lt;in T1, out TResult&gt;)。

我尝试使用协变/逆变类型创建新的委托类型,并更改接口以接受此类型的参数,但无济于事(同样的错误)。

public delegate TOut F<in TDlgIn, out TDlgOut>(TDlgIn input);

public interface IInterface<in TIn, out TOut> {
  IInterface<TIn, TOut> DoSomethingWithFunc(F<TIn, TOut> func);
}

我有办法让编译器满意吗? 这是否可能(例如使用其他嵌套类型或其他泛型参数)?如果没有,为什么不呢?

【问题讨论】:

  • 不确定this discussion 是否适用,但可能适用。
  • 我想,这与我之前问过的一个问题有关:stackoverflow.com/questions/6126741/…
  • 你试过delegate TOut F&lt;out TDlgIn, in TDlgOut&gt;(TDlgIn input)吗?在传递代表时,协方差需要反过来。

标签: c# .net generics generic-variance


【解决方案1】:

这不安全,因为你可以用它来做:

public class Id<I, O> : IInterface<I, O>
{
    private Func<I, O> f;
    public Id(Func<I, O> f) { this.f = f; }
    public IInterface<I, O> DoSomething(I i) { this.f(i); return this; }
    public IInterface<I, O> DoSomethingWithFunc(Func<I, O> newF) {
        this.f = newF;
        return this;
    }
}

然后

Func<Animal, string> fa;
IInterface<object, string> oi = new Id<object, string>(_ => "");
Interface<Monkey, string> mi = oi;  //safe
IInterface<Monkey, string> mi2 = mi.DoSomethingWithFunc(fa);
oi.DoSomething("not an animal!");

此时您已经将string 传递给Func&lt;Animal, string&gt;

【讨论】:

    【解决方案2】:

    你试过了吗?

    delegate TOut F<out TDlgIn, in TDlgOut>(TDlgIn input)
    

    在传递代表时,协方差/逆变方差需要反过来。我不知道它是否有帮助。不知道您可能想在该方法中做什么。

    【讨论】:

    • 是的,这显然可行。但我希望我的输入是输入而不是输入/输出交换。我认为这不会达到我的预期。不过我得先试一试才能下结论。也许它可以满足我的需要,但非常不直观。
    • 其实我不能使用TDlgIn作为参数,因为它被标记为out (co-variant)
    • 当您考虑足够长的时间时,它实际上直观的(除了整个事情超出任何直觉的事实......)。你的班级有一个代表,通常想给它打电话。所以要传递参数,它需要兼容。将参数传递给委托时,它会退出您的类,因此是out。你从代表那里得到的东西是进入你的班级,所以它是in
    • 你是对的(这很好地解释了它)。也许我需要一个具有 4 个类型参数的委托 :) – 希望 C# 能够推断它们,否则使用该委托将变得非常麻烦。
    • 我还是不知道你到底想做什么。
    【解决方案3】:
    Error   CS1961  Invalid variance: The type parameter 'TIn' must be covariantly valid on 'IInterface<TIn, TOut>.DoSomethingWithFunc(Func<TIn, TOut>)'. 'TIn' is contravariant.
    

    您实际上想要做的是将协变 TOut 类型作为参数传递给“DoSomethingWithFunc”方法。这是不可能的,Only In 类型只能作为参数传递,Out 只能作为结果传递。在您的示例中,您将 TOut 作为参数(它将传递给“DoSomethingWithFunc”,因为 TOut 是您的 Func 的结果)。

    网上有很多关于它的文章(“协变有效”是什么意思,但我认为最好的解释是:https://blogs.msdn.microsoft.com/ericlippert/2009/12/03/exact-rules-for-variance-validity/

    这意味着您当然可以将 Func 作为方法的结果放在您的界面中。

    【讨论】:

    • 此外,如果您想反转类型以欺骗编译器(实际上只会更改输出类型,因此可能对您没有帮助),您可以使用 Func>
    • 使用 Func> 看起来很有趣,会试一试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多