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