Co/contravariance 以 Func<in T1, out TResult> 作为参数
Co/contravariance with Func<in T1, out TResult> as parameter
假设我有一个接口,例如
public interface IInterface<in TIn, out TOut> {
IInterface<TIn, TOut> DoSomething(TIn input);
}
TIn
是 contra-变体,TOut
是 co-变体。
现在,我希望调用者能够指定一些要对输入值执行的函数,所以天真地我会在接口中添加以下方法:
IInterface<TIn, TOut> DoSomethingWithFunc(Func<TIn, TOut> func);
哪个……行不通。 TIn
现在要求是协变的,TOut
是逆变的。
我明白,我不能使用协变泛型类型作为方法的输入,但我想我可以在嵌套泛型类型中使用它们,它本身指定了方差 (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);
}
我有办法让编译器满意吗? 这甚至可能吗(例如其他嵌套类型,或额外的泛型参数)?如果不是,为什么不呢?
这不安全,因为您可以用它来做:
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<Animal, string>
。
你试过吗?
delegate TOut F<out TDlgIn, in TDlgOut>(TDlgIn input)
传递委托时,co/contra-variance 需要反过来。我不知道它是否有帮助。不知道您可能想在该方法中做什么。
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 的结果)。
网上有很多关于它的文章('covariantly valid'是什么意思,但我认为最好的解释是:https://blogs.msdn.microsoft.com/ericlippert/2009/12/03/exact-rules-for-variance-validity/
这意味着您当然可以将 Func 作为接口中方法的结果。
假设我有一个接口,例如
public interface IInterface<in TIn, out TOut> {
IInterface<TIn, TOut> DoSomething(TIn input);
}
TIn
是 contra-变体,TOut
是 co-变体。
现在,我希望调用者能够指定一些要对输入值执行的函数,所以天真地我会在接口中添加以下方法:
IInterface<TIn, TOut> DoSomethingWithFunc(Func<TIn, TOut> func);
哪个……行不通。 TIn
现在要求是协变的,TOut
是逆变的。
我明白,我不能使用协变泛型类型作为方法的输入,但我想我可以在嵌套泛型类型中使用它们,它本身指定了方差 (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);
}
我有办法让编译器满意吗? 这甚至可能吗(例如其他嵌套类型,或额外的泛型参数)?如果不是,为什么不呢?
这不安全,因为您可以用它来做:
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<Animal, string>
。
你试过吗?
delegate TOut F<out TDlgIn, in TDlgOut>(TDlgIn input)
传递委托时,co/contra-variance 需要反过来。我不知道它是否有帮助。不知道您可能想在该方法中做什么。
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 的结果)。
网上有很多关于它的文章('covariantly valid'是什么意思,但我认为最好的解释是:https://blogs.msdn.microsoft.com/ericlippert/2009/12/03/exact-rules-for-variance-validity/
这意味着您当然可以将 Func 作为接口中方法的结果。