C# return类型协变和里氏代换原则

C# return type covariance and Liskov substitution principle

我正在尝试了解协方差和 LSP。 From this question I can see C# does not support return type covariance. However Liskov substitution principle 对 return 类型施加协方差。

这是否意味着无法在 C# 中应用此原理?还是我误会了什么?

由于C#不支持return类型协变,所以return类型协变不可能违反Liskov原则。

关于 C# 中 S.O.L.I.D 原则的讨论是很好的来源:https://youtu.be/gwIS9cZlrhk?t=1886

C# 仍然可以应用 Liskov 替换原则。

考虑:

public class Base1
{
}

public class Derived1 : Base1
{
}

public class Base2
{
    public virtual Base1 Method()
    {
        return new Base1();
    }
}

public class Derived2 : Base2
{
    public override Base1 Method()
    {
        return new Derived1();
    }
}

如果 C# 支持协变 return 类型,那么 Base2Method() 的覆盖可以这样声明:

public class Derived2 : Base2
{
    public override Derived1 Method()
    {
        return new Derived1();
    }
}

C# 不允许这样做,您必须将 return 类型声明为与基 class 中相同的类型,即 Base1.

但是,这样做并不违反里氏替换原则。

考虑一下:

Base2 test = new Base2();
Base1 item = test.Method();

相比于:

Base2 test = new Derived2();
Base1 item = test.Method();

我们完全可以将 new Base2() 替换为 new Derived2(),没有任何问题。这符合 Liskov 替换原则。

C# 通过泛型和 out 泛型修饰符对此功能提供有限支持。 (参见:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-generic-modifier

支持有限,因为它只适用于接口。要重写已接受的答案:

public class Base1
{
}

public class Derived1 : Base1
{
}

public interface Base2<out T> where T : Base1
{
    T Method();
}

public class Derived2 : Base2<Derived1>
{
    public Derived1 Method()
    {
        return new Derived1();
    }
}

在这种情况下,Derived2 不仅会实现 Base2<Derived1>,还会实现 Base2<Base1>

原理生效是因为如果你通过Base2<Base1>接口调用Method它会有Base1return类型,但是如果你通过[=13调用它=] 它将具有 Derived1 return 类型。

同样,您可以使用 in 泛型修饰符实现参数逆变: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-generic-modifier

注意,不能违背原则。如果在上面的示例中将 out 关键字更改为 in 关键字,则源代码将无法编译,并且会出现以下错误:

Error   CS1961  Invalid variance: The type parameter 'T' must be covariantly valid on 'Base2<T>.Method()'. 'T' is contravariant.