成员隐藏,它的实际目的是什么

本文关键字:是什么 成员 隐藏 | 更新日期: 2024-10-25 04:06:04

'new'修饰符的实际用途是什么?

public class Base
{
    public void Say()
    {
        Console.WriteLine("Base");
    }
}
public class Derived:Base
{
    public new void Say()
    {
        Console.WriteLine("Derived");
    }
}

如果这只是编译失败不是更好吗?此代码:

        Derived d = new Derived();
        d.Say();
        ((Base)d).Say();

返回

 Derived
 Base

这不是打破了利斯科夫替代原则吗?

干杯。

成员隐藏,它的实际目的是什么

关于 LSP

这不会破坏LSP。LSP指出,如果DerivedBase的子类型,那么任何依赖于Base的代码(例如,具有Base参数的方法,如void DoSomething(Base b))都可以替换为Derived的实例,而没有任何令人惊讶的效果。

正如您所指出的,如果将Derived实例分配给Base变量,则将调用Base实现。

这是预期的行为,因为Say不是虚拟的。这意味着针对 Base 变量编写的代码需要调用Base实现。

实用目的

您可以将new方法视为规避不可覆盖方法的一种方式 - 但需要注意!您必须针对该特定类型进行编程 - 而不是其接口。

派生类中的方法如果具有相同的名称和参数列表,则被视为与其基类中的方法"相关"。编译器设计人员有两种处理此类相关方法的方法:

  1. 依赖约定 - 例如,他们可以声明此类方法为覆盖;Java就是这样做的。
  2. 请求程序员的明确指示 - C# 设计人员采取了这条路线:他们要求程序员使用 virtual/override 指定相关方法,并使用 new 指定不相关的方法。

第一种处理兴高采烈的方法让程序员别无选择:如果他们想要一个不相关的方法,他们必须给它一个不同的名称。第二种方式将选择权留给程序员,代价是更加冗长。

实质上,new 关键字允许您向编译器传达您添加的方法与基类中具有相同名称和参数的方法无关。

这不是打破了利斯科夫替代原则吗?

可以说,事实并非如此:派生类引入了具有相同名称和参数的方法,程序员明确将其指定为与基中的方法无关,这一事实在派生类用作其基类的替身的情况下不会改变派生类的行为中的任何内容。

它只是允许用户在基类中重新定义方法。 当然,如果开发人员预见到这种情况,你会希望他们将其编码为虚拟(对于默认实现)或抽象(需要指定任何实现)。

LSP 简单地说,只需要基类和子类可以互换使用,因此据我所知,这并不违反原则。

C# 的一个缺点是只能继承一个类,因此对于首选接口的多个实现的复杂系统来说,这种情况应该相对罕见。

DerivedBase和客户端不受您的控制时,这可能很有用。假设您从这个开始。


public class Base
{
  // there is no Say method in Base!
}
public class Derived:Base
{
    public /*new*/ void Say() // we don't need new here
    {
        Console.WriteLine("Derived");
    }
}

然后有一天,负责Base的人在那里添加了很酷Say方法。你可以重命名Derived.Say,但它已经被你无法更改的代码在其他地方使用。因此,您可以使用new来避免Derived中的重大更改。


public class Base
{
    public void Say()
    {
        Console.WriteLine("Base");
    }
}
public class Derived:Base
{
    public new void Say()
    {
        Console.WriteLine("Derived");
    }
}
public class SomeClient
{
    public void Run()
    {
        var d = new Derived();
        d.Say();
    }
}
虽然这样做不

是一个好的做法,但我可以看到当你从第三方程序集继承(即你无法控制的代码)并且你想要对方法有不同的行为时,它很有用。