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 类型,那么 Base2
中 Method()
的覆盖可以这样声明:
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
它会有Base1
return类型,但是如果你通过[=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.
我正在尝试了解协方差和 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 类型,那么 Base2
中 Method()
的覆盖可以这样声明:
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
它会有Base1
return类型,但是如果你通过[=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.