你能解释一下里氏替换原则有很好的C#示例?很好、里氏、你能、示例

2023-09-02 01:31:35 作者:甜味拾荒者

你能解释里氏替换原则(固态'L')与覆盖在一个简化的方式的原则的所有方面一个很好的C#示例?如果真的是可能的。

Can you explain Liskov Substitution Principle (The 'L' of SOLID) with a good C# example covering all aspects of the principle in a simplified way? If it is really possible.

推荐答案

(这个答案已经被改写2013年5月13日,阅读评论底部的讨论)的

LSP是关于以下的基类的合同。

LSP is about following the contract of the base class.

您可以如不能扔在子类作为一个使用基类不会想到,新的异常。也是一样的,如果基类抛出 ArgumentNullException 如果一个参数丢失和子类允许参数为空,也是一个LSP冲突。

You can for instance not throw new exceptions in the sub classes as the one using the base class would not expect that. Same goes for if the base class throws ArgumentNullException if an argument is missing and the sub class allows the argument to be null, also a LSP violation.

下面是一类结构违反LSP的例子:

Here is an example of a class structure which violates LSP:

public interface IDuck
{
   void Swim();
   // contract says that IsSwimming should be true if Swim has been called.
   bool IsSwimming { get; }
}
public class OrganicDuck : IDuck
{
   public void Swim()
   {
      //do something to swim
   }

   bool IsSwimming { get { /* return if the duck is swimming */ } }
}
public class ElectricDuck : IDuck
{
   bool _isSwimming;

   public void Swim()
   {
      if (!IsTurnedOn)
        return;

      _isSwimming = true;
      //swim logic  

   }

   bool IsSwimming { get { return _isSwimming; } }
}

和调用code

void MakeDuckSwim(IDuck duck)
{
    duck.Swim();
}

正如你所看到的,有鸭子的两个例子。其中有机鸭,一电一鸭。如果它开启电动鸭子只能游泳。这打破了LSP原则,因为它必须打开,以便能够游泳的 IsSwimming (这也是合同的一部分)将不会被设置为在基类。

As you can see, there are two examples of ducks. One organic duck and one electric duck. The electric duck can only swim if it's turned on. This breaks the LSP principle since it must be turned on to be able to swim as the IsSwimming (which also is part of the contract) won't be set as in the base class.

您当然可以做这样的事情解决呢

You can of course solve it by doing something like this

void MakeDuckSwim(IDuck duck)
{
    if (duck is ElectricDuck)
        ((ElectricDuck)duck).TurnOn();
    duck.Swim();
}

但是,这将打破开/关的原则,而且必须到处实现(thefore仍然会产生不稳定的code)。

But that would break Open/Closed principle and has to be implemented everywhere (and thefore still generates unstable code).

正确的解决方法是自动开启的游泳方法鸭,这样做使电动鸭子行为完全由 IDuck 接口

The proper solution would be to automatically turn on the duck in the Swim method and by doing so make the electric duck behave exactly as defined by the IDuck interface

更新

有人加了注释,删除它。它有,我想解决一个正确的观点:

Someone added a comment and removed it. It had a valid point that I'd like to address:

通过打开游泳法里面的鸭子与实际执行( ElectricDuck )。但是,这可以通过使用显式接口实现的解决。恕我直言,这更可能是您不是在游泳将其获得的问题,因为它的预期,这将在使用游 IDuck 接口

The solution with turning on the duck inside the Swim method can have side effects when working with the actual implementation (ElectricDuck). But that can be solved by using a explicit interface implementation. imho it's more likely that you get problems by NOT turning it on in Swim since it's expected that it will swim when using the IDuck interface

更新2

改写一些地区,使之更加清晰。

Rephrased some parts to make it more clear.