与Func <in T1,out TResult>作为参数的Co/contravariance

kni*_*ttl 11 .net c# generics generic-variance

假设我有一个界面如

public interface IInterface<in TIn, out TOut> {
  IInterface<TIn, TOut> DoSomething(TIn input);
}
Run Code Online (Sandbox Code Playgroud)

TIn禁忌 -variant,并TOut合作 -variant.

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

IInterface<TIn, TOut> DoSomethingWithFunc(Func<TIn, TOut> func);
Run Code Online (Sandbox Code Playgroud)

哪......不起作用.TIn现在需要协变,TOut逆变.

我理解,我不能使用协变泛型类型作为方法的输入,但我想我可以在嵌套泛型类型中使用它们,它本身指定了variance(Func<in T1, out TResult>).

我尝试使用co/contravariant类型创建一个新的委托类型,并更改接口以接受此类型的参数,但无效(相同的错误).

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);
}
Run Code Online (Sandbox Code Playgroud)

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

Lee*_*Lee 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

进而

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!");
Run Code Online (Sandbox Code Playgroud)

此时您将把 a 传递string给 an Func<Animal, string>