泛型的两种用法有什么区别(协变)
What is the difference between the two uses of generics (covariance)
我想我明白泛型不支持协变,这就是为什么第一个示例不起作用并给出 Invalid Cast 异常的原因。不过一切都可以编译。
但为什么第二个示例有效?只能看出是同一个东西
MyClass是这样的:
public interface IGenericClass<T> { }
public class MyClass : IGenericClass<SomeType>
{
}
不工作:
public class SendingEmail<T>
{
IGenericClass<T> abc;
public void Send(IGenericClass<T> _abc)
{
this.abc = _abc;
}
}
用法:
var myClass = new MyClass();
SendingEmail<MyClass> sendingEmail = new SendingEmail<MyClass>();
sendingEmail.Send(IGenericClass<MyClass>myClass);
//sendingEmail.Send(myClass); This was wrong
也尝试过:
已删除,因为它从未编译过
工作:
class SendingEmail
{
void Send<T>(MyGenericClass<T> abc)
{
}
}
用法:
SendingEmail sendingEmail = new SendingEmail();
sendingEmail.Send(myclass);
第一个例子与协方差无关。您只是混淆了 T
的真正含义; T
是 SomeType
,不是 MyClass
。
在第二个示例中,您让编译器正确推断类型,这就是它起作用的原因。
编辑
修改你的问题并没有解决问题,你还在混淆 T
是什么:在 MyClass
中 T
是 SomeType
。在 SendingEmail<T>
T
中 也 应该是 SomeType
因为 abc
是 IGenericType<T>
并且你最终希望它是 IGenericType<SomeType>
.
在您的代码中,您正在创建类型为 SendingEMail<MyClass>
的实例,这意味着 abc
确实类型为 IGenericType<MyClass>
,这是错误的;您不能将 IGenericType<SomeType>
转换为 IGenericType<IGenericType<SomeClass>>
,而这正是您在调用 sendingEMail(MyClass)
;
时真正做的事情
您需要做的是:var sendingEmail = new SendingEmail<SomeType>()
.
关于这与类型差异有关,事实并非如此。例如,类型差异允许您执行 IEnumerable<Animal> animals = Enumerable.Emtpy<Tiger>()
而不允许您对 IList<T>
执行相同的操作; IEnumerable
在 T
中是协变的,而 IList
在 T
中是不变的。
请注意,C# 确实破坏了数组中的协变性,以下是合法的:Animal[] animals = new Tiger[3]
。它坏了,因为现在你可以合法地做 animals[0] = new Turtle();
并且......哎哟......你只是遇到了一个运行时异常。这种场景正是IList
在T
中不变的原因。为什么 C# 破坏了数组中的协变性?我不知道,但我认为这是一个不幸的(尽管可能是合理的)设计决定。
我想我明白泛型不支持协变,这就是为什么第一个示例不起作用并给出 Invalid Cast 异常的原因。不过一切都可以编译。
但为什么第二个示例有效?只能看出是同一个东西
MyClass是这样的:
public interface IGenericClass<T> { }
public class MyClass : IGenericClass<SomeType>
{
}
不工作:
public class SendingEmail<T>
{
IGenericClass<T> abc;
public void Send(IGenericClass<T> _abc)
{
this.abc = _abc;
}
}
用法:
var myClass = new MyClass();
SendingEmail<MyClass> sendingEmail = new SendingEmail<MyClass>();
sendingEmail.Send(IGenericClass<MyClass>myClass);
//sendingEmail.Send(myClass); This was wrong
也尝试过:
已删除,因为它从未编译过
工作:
class SendingEmail
{
void Send<T>(MyGenericClass<T> abc)
{
}
}
用法:
SendingEmail sendingEmail = new SendingEmail();
sendingEmail.Send(myclass);
第一个例子与协方差无关。您只是混淆了 T
的真正含义; T
是 SomeType
,不是 MyClass
。
在第二个示例中,您让编译器正确推断类型,这就是它起作用的原因。
编辑
修改你的问题并没有解决问题,你还在混淆 T
是什么:在 MyClass
中 T
是 SomeType
。在 SendingEmail<T>
T
中 也 应该是 SomeType
因为 abc
是 IGenericType<T>
并且你最终希望它是 IGenericType<SomeType>
.
在您的代码中,您正在创建类型为 SendingEMail<MyClass>
的实例,这意味着 abc
确实类型为 IGenericType<MyClass>
,这是错误的;您不能将 IGenericType<SomeType>
转换为 IGenericType<IGenericType<SomeClass>>
,而这正是您在调用 sendingEMail(MyClass)
;
您需要做的是:var sendingEmail = new SendingEmail<SomeType>()
.
关于这与类型差异有关,事实并非如此。例如,类型差异允许您执行 IEnumerable<Animal> animals = Enumerable.Emtpy<Tiger>()
而不允许您对 IList<T>
执行相同的操作; IEnumerable
在 T
中是协变的,而 IList
在 T
中是不变的。
请注意,C# 确实破坏了数组中的协变性,以下是合法的:Animal[] animals = new Tiger[3]
。它坏了,因为现在你可以合法地做 animals[0] = new Turtle();
并且......哎哟......你只是遇到了一个运行时异常。这种场景正是IList
在T
中不变的原因。为什么 C# 破坏了数组中的协变性?我不知道,但我认为这是一个不幸的(尽管可能是合理的)设计决定。