C# 协方差和反方差中的类型安全性
我正在深度阅读乔恩·斯凯特的作品。虽然我已经理解了协方差和逆变换的概念,但我无法理解这一行: 当某个类型只描述 返回类型参数,当某个类型 仅描述接受类型参数的操作C# 协方差和反方差中的类型安全性,c#,C#,我正在深度阅读乔恩·斯凯特的作品。虽然我已经理解了协方差和逆变换的概念,但我无法理解这一行: 当某个类型只描述 返回类型参数,当某个类型 仅描述接受类型参数的操作 IEnumerable<IShape> GetShapes() { IEnumerable<Circle> circles = GetEnumerableOfCircles(); return circles; // Conversion from IEnumerable<Circle&g
IEnumerable<IShape> GetShapes()
{
IEnumerable<Circle> circles = GetEnumerableOfCircles();
return circles; // Conversion from IEnumerable<Circle> to IEnumerable<IShape> - COVARIANCE
}
void SomeMethod()
{
IEnumerable<Circle> circles = GetEnumerableOfCircles();
DoSomethingWithShapes(circles); // Conversion from IEnumerable<Circle> to IEnumerable<IShape> - COVARIANCE
}
void DoSomethingWithShapes(IEnumerable<IShape> shapes) // Why this COVARIANCE is type unsafe??
{
// do something with Shapes
}
IEnumerable<Circle> GetShapes()
{
IEnumerable<IShape> shapes = GetEnumerableOfIShapes();
return shapes; // Conversion from IEnumerable<IShape> to IEnumerable<Circle> - Contra-Variance
// Why this Contra-Variance is type unsafe??
}
void SomeMethod()
{
IEnumerable<IShape> shapes = GetEnumerableOfIShapes();
DoSomethingWithCircles(shapes); // Conversion from IEnumerable<IShape> to IEnumerable<Circle> - Contra-Variance
}
void DoSomethingWithCircles(IEnumerable<Circle> circles)
{
// do something with Circles
}
有人能用这两个方面的例子解释一下,为什么两个方向都是类型安全的,而不是另一个方向
更新问题:
从给出的答案来看,我还是不明白。我将尝试使用《深入》一书中的相同示例来解释我的担忧
它解释了如何使用以下类层次结构:
协方差是:尝试从IEnumerable
转换为IEnumerable
,但需要指出的是,只有当我们从某个方法返回时,此转换才是类型安全的,而当我们将其作为IN参数传递时,则不是类型安全的
IEnumerable<IShape> GetShapes()
{
IEnumerable<Circle> circles = GetEnumerableOfCircles();
return circles; // Conversion from IEnumerable<Circle> to IEnumerable<IShape> - COVARIANCE
}
void SomeMethod()
{
IEnumerable<Circle> circles = GetEnumerableOfCircles();
DoSomethingWithShapes(circles); // Conversion from IEnumerable<Circle> to IEnumerable<IShape> - COVARIANCE
}
void DoSomethingWithShapes(IEnumerable<IShape> shapes) // Why this COVARIANCE is type unsafe??
{
// do something with Shapes
}
IEnumerable<Circle> GetShapes()
{
IEnumerable<IShape> shapes = GetEnumerableOfIShapes();
return shapes; // Conversion from IEnumerable<IShape> to IEnumerable<Circle> - Contra-Variance
// Why this Contra-Variance is type unsafe??
}
void SomeMethod()
{
IEnumerable<IShape> shapes = GetEnumerableOfIShapes();
DoSomethingWithCircles(shapes); // Conversion from IEnumerable<IShape> to IEnumerable<Circle> - Contra-Variance
}
void DoSomethingWithCircles(IEnumerable<Circle> circles)
{
// do something with Circles
}
IEnumerable GetShapes()
{
IEnumerable circles=GetEnumerableOfCircles();
返回圆;//从IEnumerable到IEnumerable的转换-协方差
}
void方法()
{
IEnumerable circles=GetEnumerableOfCircles();
DoSomethingWithShapes(圆);//从IEnumerable到IEnumerable的转换-协方差
}
void DoSomethingWithShapes(IEnumerable shapes)//为什么这种协方差是类型不安全的??
{
//用形状做点什么
}
相反的情况是:尝试从IEnumerable
转换为IEnumerable
,只有在将其作为IN参数发送时执行此操作时,才会提到它是类型安全的
IEnumerable<IShape> GetShapes()
{
IEnumerable<Circle> circles = GetEnumerableOfCircles();
return circles; // Conversion from IEnumerable<Circle> to IEnumerable<IShape> - COVARIANCE
}
void SomeMethod()
{
IEnumerable<Circle> circles = GetEnumerableOfCircles();
DoSomethingWithShapes(circles); // Conversion from IEnumerable<Circle> to IEnumerable<IShape> - COVARIANCE
}
void DoSomethingWithShapes(IEnumerable<IShape> shapes) // Why this COVARIANCE is type unsafe??
{
// do something with Shapes
}
IEnumerable<Circle> GetShapes()
{
IEnumerable<IShape> shapes = GetEnumerableOfIShapes();
return shapes; // Conversion from IEnumerable<IShape> to IEnumerable<Circle> - Contra-Variance
// Why this Contra-Variance is type unsafe??
}
void SomeMethod()
{
IEnumerable<IShape> shapes = GetEnumerableOfIShapes();
DoSomethingWithCircles(shapes); // Conversion from IEnumerable<IShape> to IEnumerable<Circle> - Contra-Variance
}
void DoSomethingWithCircles(IEnumerable<Circle> circles)
{
// do something with Circles
}
IEnumerable GetShapes()
{
IEnumerable shapes=GetEnumerableOfIShapes();
返回形状;//从IEnumerable到IEnumerable的转换-逆变换
//为什么这种对冲是不安全的??
}
void方法()
{
IEnumerable shapes=GetEnumerableOfIShapes();
DoSomethingWithCircles(shapes);//从IEnumerable到IEnumerable的转换-反向方差
}
void DoSomethingWithCircles(IEnumerable circles)
{
//用圆圈做点什么
}
想象你制作了一个ILogger
界面,它知道如何记录T
的详细信息。假设您有一个Request
类和一个ExpeditedRequest
子类。当然,ILogger
应该可以转换为ILogger
。毕竟,它可以记录任何请求
Interface ILogger<in T> where T: Request {
void Log(T arg);
}
接口ILogger,其中T:Request{
无效日志(T arg);
}
现在想象另一个接口IRequestProducer
,它获取某个队列中的下一个请求。在您的系统中有不同的请求源,当然,其中一些仍然可以有不同的子类。在这种情况下,我们不能依赖于将IRequestProducer
转换为IRequestProducer
,因为它可能会生成非加速请求。但反向转换会起作用
Interface IRequestProducer<T> where T: Request {
T GetNextRequest();
}
接口IRequestProducer,其中T:Request{
T GetNextRequest();
}
协方差
当SomeType仅描述返回类型参数的操作时,协方差是安全的
IEnumerable
接口可能是协方差的最常见示例。它是安全的,因为它只返回T
类型的值(特别是IEnumerator
,但不接受任何T
对象作为参数)
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
如果您有一个名为base
的基类和一个名为derived
的派生类,那么您可以执行以下操作:
IEnumerable<Derived> derivedItems = Something();
IEnumerable<Base> baseItems = derivedItems;
Action<Base> baseAction = b => b.DoSomething()
Action<Derived> derivedAction = baseAction;
Derived d = new Derived();
// These 2 lines do the same thing:
baseAction(d);
derivedAction(d);
这是不安全的,因为不能保证Base
的每个实例也是派生的
的实例
逆变
当SomeType仅描述接受类型参数的操作时,逆变是安全的
操作
委托是一个很好的逆变示例
public delegate void Action<in T>(T obj);
这是因为将Derived
的实例传递给baseAction
是完全可以接受的。但是,它不会反过来工作:
Action<Derived> derivedAction = d => d.DoSomething()
Action<Base> baseAction = derivedAction; // No good!
Base b = new Base();
baseAction(b); // This is OK.
derivedAction(b); // This does not work because b may not be an instance of Derived!
Action-derivedAction=d=>d.DoSomething()
Action baseAction=derivedAction;//不好!
基b=新基();
baseAction(b);//这没关系。
derivedAction(b);//这不起作用,因为b可能不是派生的实例!
这是不安全的,因为无法保证
Base
的实例也将是派生的的实例。我从MSDN中阅读了以下两页后了解到:
事实上,我想当我读到书中的C#4
部分时,从这本书中也会变得很清楚,它将解释in
和out
关键字的类型参数
泛型
。现在,我正在阅读bo的C#
部分中的泛型的局限性嗯
我想理解的陈述是:
当某个类型只描述
返回类型参数,当某个类型
仅描述接受类型参数的操作
IEnumerable<IShape> GetShapes()
{
IEnumerable<Circle> circles = GetEnumerableOfCircles();
return circles; // Conversion from IEnumerable<Circle> to IEnumerable<IShape> - COVARIANCE
}
void SomeMethod()
{
IEnumerable<Circle> circles = GetEnumerableOfCircles();
DoSomethingWithShapes(circles); // Conversion from IEnumerable<Circle> to IEnumerable<IShape> - COVARIANCE
}
void DoSomethingWithShapes(IEnumerable<IShape> shapes) // Why this COVARIANCE is type unsafe??
{
// do something with Shapes
}
IEnumerable<Circle> GetShapes()
{
IEnumerable<IShape> shapes = GetEnumerableOfIShapes();
return shapes; // Conversion from IEnumerable<IShape> to IEnumerable<Circle> - Contra-Variance
// Why this Contra-Variance is type unsafe??
}
void SomeMethod()
{
IEnumerable<IShape> shapes = GetEnumerableOfIShapes();
DoSomethingWithCircles(shapes); // Conversion from IEnumerable<IShape> to IEnumerable<Circle> - Contra-Variance
}
void DoSomethingWithCircles(IEnumerable<Circle> circles)
{
// do something with Circles
}
除了安全之外,也不可能以其他方向编写接口方法,因为编译器会抱怨,如msdn上的上述两页所述:
如果类型仅用作方法参数的类型,而不用作方法返回类型,则可以在泛型接口或委托中声明为逆变。
在泛型接口中,如果类型参数满足以下条件,则可以将其声明为协变:
type参数仅用作接口方法的返回类型,而不用作方法参数的类型。
现在有两点让我对这句话感到困惑:
首先,我误解了声明本身——我认为t