C# return type covariance and Liskov substitution principle

你说的曾经没有我的故事 提交于 2019-11-28 03:56:57

问题


I'm trying to understand Covariance and LSP. From this question I can see C# does not support return type covariance. However Liskov substitution principle imposes covariance on return type.

Does it mean it is impossible to apply this principle in C# ? Or did I missunderstand something ?


回答1:


C# can still apply the Liskov substitution principle.

Consider:

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();
    }
}

If C# supported covariant return types, then the override for Method() in Base2 could be declared thusly:

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

C# does not allow this, and you must declare the return type the same as it is in the base class, namely Base1.

However, doing so does not make it violate the Liskov substitution principle.

Consider this:

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

Compared to:

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

We are completely able to replace new Base2() with new Derived2() with no issues. This complies with the Liskov substitution principle.




回答2:


Since return type covariance is not supported in C#, it is impossible to violate the Liskov principle when it comes to return type covariance.

Good source is this talk of S.O.L.I.D principles in C#: https://youtu.be/gwIS9cZlrhk?t=1886




回答3:


C# do have a limited support for this feature through generics and the out generic modifier. (See: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-generic-modifier)

The support is limited, because it only works with interfaces. To rewrite the accepted answer:

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();
    }
}

In this case Derived2 will not only implement Base2<Derived1> but it will also implement Base2<Base1>.

The principle takes effect because if you call Method through the Base2<Base1> interface it will have Base1 return type, but if you call it through Derived2 it will have Derived1 return type.

Similarly you can implement parameter contravariance with the in generic modifier: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-generic-modifier

Note, that it is not possible to violate the principle. If in the above example you change the out keyword to the in keyword, the source will not compile, and you will get the following error:

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


来源:https://stackoverflow.com/questions/43892239/c-sharp-return-type-covariance-and-liskov-substitution-principle

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!