Variance 是关于能够用比最初声明的更多或更少派生类型替换类型参数。例如,IEnumerable<T> 与 T 是协变的,这意味着如果您从对 IEnumerable<U> 对象的引用开始,您可以将该引用分配给类型为 IEnumerable<V> 的变量,其中 V 可从 @987654326 分配@(例如 U 继承 V)。这是可行的,因为任何尝试使用IEnumerable<V> 的代码都希望只接收V 的值,并且由于V 可以从U 分配,因此只接收U 的值也是有效的。
对于像T 这样的协变参数,您必须分配给目标类型与T 相同的类型,或者可以从T 分配。对于逆变参数,它必须走另一条路。目标类型必须与类型参数相同或可分配给类型参数。
那么,您尝试编写的代码在这方面是如何工作的?
当您声明Test<in TIn, out TOut> 时,您承诺将该接口Test<TIn, TOut> 的实例分配给具有Test<U, V> 类型的任何目标是有效的,其中U 可以分配给TIn 和@ 987654342@ 可以分配给V(当然,它们是相同的)。
同时,让我们考虑一下您的transform 代表的期望。 Func<T, TResult> 类型差异要求如果您想将该值分配给其他东西,它也符合差异规则。也就是说,目标Func<U, V> 必须具有可从T 分配的U,以及可从V 分配的TResult。这可确保您的委托目标方法(期望接收 U 的值)将获得其中之一,并且该方法返回的类型为 V 的值可以被接收它的代码接受。
重要的是,您的接口方法F() 是接收的方法! 接口声明承诺TOut 将仅用作接口成员的输出。但是通过使用transform 委托,F() 方法将接收一个TOut 的值,使该方法输入。同样,F() 方法允许将 TIn 的值传递给 transform 委托,使其成为接口实现的输出,即使您已承诺 TIn仅用作输入。
换句话说,每一层调用都会反转变化的感觉。接口中的成员必须只使用协变类型参数作为输出,只使用逆变参数作为输入。但是,当这些参数在传递给接口成员或从接口成员返回的委托类型中使用时,它们在意义上变得相反,并且必须遵守这方面的差异。
一个具体的例子:
假设我们有一个你的接口的实现,Test<object, string>。如果编译器允许您的声明,您将被允许将该实现的值Test<object, string> 分配给具有Test<string, object> 类型的变量。也就是说,最初的实现承诺允许任何具有object 类型的东西作为输入,并且只返回具有string 类型的值。声明为 Test<string, object> 的代码使用它是安全的,因为它会将 string 对象传递给需要 objects 值的实现(string 是 object),并且它将接收具有类型的值object 来自返回 string 值的实现(同样,string 是 object,因此也很安全)。
但是您的接口实现需要代码传递Func<object, string> 类型的委托。如果您被允许将(如上所述)您的接口实现视为Test<string, object>,那么使用您的重铸实现的代码将能够将Func<string, object> 的委托传递给方法F()。实现中的方法F() 允许将object 类型的任何值传递给委托,但该委托的类型为Func<string, object>,只期望将具有string 类型的值传递给它。如果 F() 传递了其他东西,例如只是一个普通的旧new object(),委托实例将无法使用它。它期待一个string!
所以,事实上,编译器正在做它应该做的事情:它阻止你编写非类型安全的代码。正如声明的那样,如果您被允许以不同的方式使用该接口,您实际上将能够编写在编译时允许但在运行时可能会中断的代码。这与泛型的全部观点完全相反:能够在编译时确定代码是类型安全的!
现在,如何解决这个难题。不幸的是,您的问题中没有足够的上下文来了解正确的方法是什么。您可能只需要放弃方差。通常,实际上不需要使类型变体。在某些情况下这很方便,但不是必需的。如果是这种情况,那么就不要让接口的参数变体。
或者,您可能确实想要变化,并认为以不同的方式使用界面是安全的。这更难解决,因为您的基本假设不正确,您需要以其他方式实现代码。如果您可以反转Func<T, TResult> 中的参数,则代码将编译。 IE。制作方法F(Func<TOut, TIn> transform)。但是您的问题中没有任何内容表明在您的场景中这实际上是可能的。
同样,如果没有更多上下文,就不可能说出“其他方式”对您有用。但是,希望现在您以您现在编写代码的方式了解代码中的危险,您可以重新审视导致您使用这种非类型安全接口声明的设计决策,并且可以提出一些可行的方法。如果您对此有疑问,请发布一个新问题,提供更多详细信息,说明您为什么认为这是安全的、您将如何使用界面、您考虑过哪些替代方案以及为什么这些都不适合您.