目前,我正准备为我的同事介绍C#中新的通用差异功能.为了简化故事,我写了以下几行:
IList<Form> formsList = new List<Form> { new Form(), new Form() };
IList<Control> controlsList = formsList;
Run Code Online (Sandbox Code Playgroud)
是的,这当然是不可能的,因为IList(Of T)是不变的(至少我的想法).编译器告诉我:
无法隐式转换
System.Collections.Generic.IList<System.Windows.Forms.Form>为System.Collections.Generic.IList<System.Windows.Forms.Control>.存在显式转换(您是否错过了演员?)
嗯,这是否意味着我可以强制进行显式转换?我刚尝试过:
IList<Form> formsList = new List<Form> { new Form(), new Form() };
IList<Control> controlsList = (IList<Control>)formsList;
Run Code Online (Sandbox Code Playgroud)
并且...它编译!这是否意味着我可以抛弃不变性? - 至少编译器没问题,但我只是将前编译时错误转为运行时错误:
Unable to cast object of type 'System.Collections.Generic.List`1[System.Windows.Forms.Form]' to type 'System.Collections.Generic.IList`1[System.Windows.Forms.Control]'.
我的问题:为什么我可以将IList<T>(或我的实验的任何其他不变界面)的不变性抛弃了?我是否真的抛弃了不变性,或者在这里发生了什么样的转换(因为IList(Of Form)并且IList(Of Control)完全不相关)?这是C#的一个黑暗角落,我不知道?
我正在尝试编写一个扩展方法,该方法将转换IDictionary<K, S<V>>保存任何类型的collection/sequence(S<V>),ILookup<K, V>在这些情况下,这是更合适的数据结构.这意味着我希望我的扩展能够在不同的S类型和接口上工作:
IDictionary<K, IEnumerable<V>>IDictionary<K, ICollection<V>>IDictionary<K, List<V>>理想情况下,我不想为每种可能的集合类型编写单独的实现,并且我希望类型推断能够完成它的工作.
我试过的是:
public static ILookup<TKey, TValue>ToLookup<TKey, TCollection, TValue>(
this IDictionary<TKey, TCollection> dictionary)
where TCollection : IEnumerable<TValue>
Run Code Online (Sandbox Code Playgroud)
但它没有TValue参数列表,所以类型推断无法弄明白 - 我得到"方法ToLookup的类型参数不能从用法推断".
是否有可能以某种方式以某种方式工作,而不是在方法中添加假的TValue参数?
预期用途的示例
我希望所有上述调用都成为可能,并导致调用我的单个扩展方法:
var dictOfIEnumerables = new Dictionary<int, IEnumerable<int>>();
var lookupFromIEnumerables = dictOfIEnumerables.ToLookup();
var dictOfICollections = new Dictionary<int, ICollection<int>>();
var lookupFromICollections = dictOfICollections.ToLookup();
var dictOfLists = new Dictionary<int, List<int>>();
var lookupFromLists = dictOfLists.ToLookup();
Run Code Online (Sandbox Code Playgroud) Scala语言规范(关于方差注释的第 4.5 节,第 44 页)说
使用上面的第一点,很容易看出(至少在形式上)
trait Covariant[+A] {
def problematic[B <: A](x : B)
}
Run Code Online (Sandbox Code Playgroud)
产生错误消息
error: covariant type A occurs in contravariant position in type >: Nothing <: A of type B
def problematic[B <: A](x : B)
Run Code Online (Sandbox Code Playgroud)
使用第一点和第二点很容易看出
trait Contravariant[-A] {
def problematic[B >: A](x : B)
}
Run Code Online (Sandbox Code Playgroud)
产生错误消息
error: contravariant type A occurs in covariant position in type >: A <: Any of type B
def problematic[B >: A](x : B)
Run Code Online (Sandbox Code Playgroud)
正如我所提到的,很容易从形式上(即遵循方差注释的规则)看出这些错误发生的原因。然而,我无法举出一个例子来说明这些限制的必要性。相反,很容易举出示例来说明为什么方法参数应该更改方差位置,请参见 …
这不会编译:
class MyClass[+A] {
def myMethod(a: A): A = a
}
//error: covariant type A occurs in contravariant position in type A of value a
Run Code Online (Sandbox Code Playgroud)
好吧,很公平。但这确实可以编译:
class MyClass[+A]
implicit class MyImplicitClass[A](mc: MyClass[A]) {
def myMethod(a: A): A = a
}
Run Code Online (Sandbox Code Playgroud)
这使我们能够规避方差检查给我们带来的任何问题:
class MyClass[+A] {
def myMethod[B >: A](b: B): B = b //B >: A => B
}
implicit class MyImplicitClass[A](mc: MyClass[A]) {
def myExtensionMethod(a: A): A = mc.myMethod(a) //A => A!!
}
val foo = new MyClass[String]
//foo: MyClass[String] …Run Code Online (Sandbox Code Playgroud) open class A
class B: A()
fun <T> copy(src: MutableList<T>, dst: MutableList<T>) {
for (i in 0 until src.size) {
dst.add(i, src[i])
}
}
Run Code Online (Sandbox Code Playgroud)
对于上面提到的代码,我理解copy function期望两个类型参数的类型完全相同。稍微修改copy(src: MutableList<T>, dst: MutableList<in T>)一下in关键字,我是说它src 必须是完全类型,T但目的地可以是Ttype T或任何超类型。
对于上述修改后的代码,我可以调用如下方法,
fun main(args: Array<String>) {
val l1 = mutableListOf(B(), B())
val l2 = mutableListOf<A>()
copy(l1, l2)
} // main
Run Code Online (Sandbox Code Playgroud)
copy(l1, l2)如果我in从目的地删除(理解),上述方法不起作用。
我的问题是,如果更新函数参数src以接受out列表的投影,我可以毫无错误地调用该函数。例如
fun …Run Code Online (Sandbox Code Playgroud) 最近我遇到了一个问题,我有一个函数必须返回一个Is数组,以 enum 的所有值的形式E,E实现 interface I,我想到的每个代码编译器都抱怨类型不匹配:
Error:(x, x) Kotlin: Type mismatch: inferred type is Array<E> but Array<I> was expected
Run Code Online (Sandbox Code Playgroud)
一个最小的例子:
interface I {}
enum class E: I {
A, B, C;
}
fun getMoreInterfaces(): Array<I> {
return E.values()
}
Run Code Online (Sandbox Code Playgroud)
当尝试分配E.values()给类型的变量时会发生这种情况,Array<I>
我很确定这应该是可能的,因为E实现了I.
我在测试时想到的另一件事是,像这样使用时它工作得很好:
interface I {}
enum class E: I {
A, B, C;
}
fun getMoreInterfaces(): Array<I> {
return arrayOf(E.A, E.B, E.C)
}
Run Code Online (Sandbox Code Playgroud)
我在这个主题上做了很多搜索,但没有运气(也许我选择了错误的描述方式?)
在SO中阅读以下问题后,我正在研究代理方差:Delegate.CreateDelegate()和泛型:绑定到目标方法的错误
我在Barry kelly找到了一段非常好的代码, 网址是https://www.blogger.com/comment.g?blogID=8184237816669520763&postID=2109708553230166434
这是(以加糖形式:-)
using System;
namespace ConsoleApplication4
{
internal class Base
{
}
internal class Derived : Base
{
}
internal delegate void baseClassDelegate(Base b);
internal delegate void derivedClassDelegate(Derived d);
internal class App
{
private static void Foo1(Base b)
{
Console.WriteLine("Foo 1");
}
private static void Foo2(Derived b)
{
Console.WriteLine("Foo 2");
}
private static T CastDelegate<T>(Delegate src)
where T : class
{
return (T) (object) Delegate.CreateDelegate(
typeof (T),
src.Target,
src.Method,
true); // throw on fail …Run Code Online (Sandbox Code Playgroud) 我写一些应该很直接的东西很难,但我似乎无法正确理解语法.
我有食物的类层次结构:
Food :> Vegetable :> Bamboo
Run Code Online (Sandbox Code Playgroud)
而对于动物:
Animal :> Herbivore :> Panda
Run Code Online (Sandbox Code Playgroud)
而我正试图在动物中定义一种吃法,这样熊猫一般不能吃蔬菜,只能吃竹子.目前,我的代码如下所示:
class Food(val name : String)
class Vegetable(name: String) extends Food(name)
class Bamboo extends Vegetable("bamboo")
class Animal[F <: Food](val name : String) {
def eat[T <: F](f : T) = println(s"$name eats some yummy ${f.name}")
}
class Herbivore[F <: Vegetable](name :String) extends Animal[Vegetable](name)
class Panda extends Herbivore[Bamboo]("panda")
Run Code Online (Sandbox Code Playgroud)
我的麻烦是,当我制作蔬菜时,熊猫可以吃它:
(new Panda) eat (new Vegetable("potato"))
Run Code Online (Sandbox Code Playgroud)
出了点问题:(
任何帮助将非常欢迎:)
我有以下代码,我正在尝试为我的域对象编写通用验证规则.这样做我有一个问题来处理Func委托支持方差
public class Person { }
public class Employee : Person { }
internal interface IValidation<T> where T: Person
{
void AddValidationRule(Func<T,bool> predicateFunction);
}
internal class BaseValidation : IValidation<Person>
{
void IValidation<Person>.RegisterValidationRules(Person person)
{
}
}
internal class EmployeeValidation : BaseValidation
{
void RegisterValidation()
{
Func<Employee,bool> empPredicate = CheckJoiningDate;
base.AddValidationRule(empPredicate);
}
bool CheckJoiningDate(Employee employee)
{
return employee.JoiningDate > DateTime.Now.AddDays(-1) ;
}
}
Run Code Online (Sandbox Code Playgroud)
使用上面的代码,编译器会给出一条错误消息
编译器错误在线:base.AddValidationRule(empPredicate); 参数1:无法从'System.Func <> Employee,bool>'转换为'System.Func <> Person,bool>
我曾参考过这个https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/dd465122%28v%3dvs.100%29但我仍然无法使编译器了解这里的逆变,
感谢您的帮助,让我更好地理解这一点
TypeScript 似乎无法推断逆变。下面是一个例子来说明这种不一致:
class Base { base = "I'm base" }
class Der extends Base { der = "I'm der" }
interface Getter<E> { get(): E }
interface Setter<E> { set(value: E): void }
type Test1 = Getter<Der> extends Getter<Base> ? 'Yes' : 'No' // "Yes"
type Test2 = Getter<Base> extends Getter<Der> ? 'Yes' : 'No' // "No"
type Test3 = Setter<Der> extends Setter<Base> ? 'Yes' : 'No' // "Yes"
type Test4 = Setter<Base> extends Setter<Der> ? 'Yes' : 'No' // …Run Code Online (Sandbox Code Playgroud) 任何人都可以解释为什么我必须转换为T,为什么Add2不接受Bar作为参数?
class Foo<T> where T : class, IBar
{
void Add1(IDo<T> @do) { @do.Stuff(new Bar() as T); }
// Add2 does not compile:
// Argument type Bar is not assignable to parameter Type T
void Add2(IDo<T> @do) { @do.Stuff(new Bar()); }
}
interface IBar {}
class Bar : IBar {}
interface IDo<in T> {
void Stuff(T bar);
}
Run Code Online (Sandbox Code Playgroud) generic-variance ×11
c# ×5
covariance ×4
generics ×3
scala ×3
kotlin ×2
.net-core ×1
c#-4.0 ×1
casting ×1
delegates ×1
enums ×1
typescript ×1