我刚刚学会了如何屏蔽一个基类成员(使用新的),但我错过了我为什么要这样做的重点。掩蔽是否像使用封装一样为我们提供了一定程度的保护?请指教。
6 回答
它不仅用于掩蔽。它实际上打破了继承链,因此如果调用基类方法,则不会调用派生类中的方法(仅调用基类中的方法)。
您实际上是在创建一个与基类方法无关的新方法。因此,“新”关键字。
请记住,如果您想定义与基类型方法具有相同签名但返回类型不同的方法,则可以使用“new”关键字。
您很少会使用“new”来掩盖基类成员。
它主要用于派生类首先具有成员,然后将其添加到基类中的情况——同名用于不同目的。new
你承认你知道你正在以不同的方式使用它。在 C++ 中添加基成员时,它只是默默地将现有方法合并到继承链中。在 C# 中,您必须在 和 之间进行选择new
,override
以表明您知道发生了什么。
我遇到的唯一 有效的 安全示例是更具体的返回类型或在属性上提供 set 访问器。我不是说这些是唯一的,但这就是我发现的全部。
例如,假设您有一个非常简单的基础,如下所示:
public abstract class Base
{
public string Name { get; protected set; }
public Base(string name)
{ Name = name; }
}
你可以有一个看起来更像这样的派生:
public class Derived : Base
{
public new string Name
{
get { return base.Name; }
set { base.Name = value; }
}
public Derived(string name) : base(name)
{ }
}
假设业务规则允许这个特定的 Derived 具有可更改的名称,我相信这是可以接受的。问题new
在于它会根据实例被视为什么类型来改变行为。例如,如果我说:
Derived d = new Derived("Foo");
d.Name = "Bar";
Base b = d;
b.Name = "Baz"; // <-- No set available.
在这个简单的例子中,我们很好。我们正在用 覆盖该行为new
,但不是以破坏性的方式。更改返回类型需要更多技巧。即,如果您使用new
更改派生类型的返回类型,则不应允许该类型由基设置。看看这个例子:
public class Base
{
public Base(Base child)
{ Child = child; }
public Base Child { get; private set; }
}
public class Derived
{
public Derived(Derived child) : base(child)
{ }
public new Derived Child
{ get { return (Derived)base.Child; } }
}
如果我能set Child
在Base
课堂上,我可能会在课堂上遇到选角问题Derived
。另一个例子:
Derived d = new Derived(someDerivedInstance);
Base b = d;
var c = b.Child; // c is of type Base
var e = d.Child; // e is of type Derived
我不能通过将我的所有Derived
类都视为基础来破坏任何业务规则,不进行类型检查和强制转换很方便。
我刚刚学会了如何屏蔽基类成员(使用新的)
仅供参考,此功能通常称为“隐藏”而不是“屏蔽”。我认为“屏蔽”是清除位数组中的位。
我错过了我为什么要这样做的重点。
通常你不想。由于某些使用和不使用此功能的原因,请参阅我 2008 年关于该主题的文章:
http://blogs.msdn.com/b/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx
掩蔽是否像使用封装一样为我们提供了一定程度的保护?
不,不是的。
您所指的称为Name Hiding。它主要是一个便利功能。如果您从一个您不控制其源的类继承,则使用new
将允许您更改方法的行为,即使它没有被声明为虚拟(或者如果它是虚拟的,则完全更改签名)。该new
关键字只是抑制编译器警告。您基本上是在通知编译器您有意隐藏父类的方法。
reintroduce
出于同样的原因,Delphi 有关键字。
除了被压制的警告之外,这还能给你带来什么?不是很多。您无法new
从父类访问该方法。如果您的子类直接实现接口(与从其父类继承它相反),您可以从接口访问它。您仍然可以从孩子那里调用父类的成员。您班级的任何其他后代都将继承该new
成员,而不是父级中的成员。
这实际上称为成员隐藏。有几个常见的场景可以适当地使用它。
- 它允许您解决版本控制问题,其中基类或派生类作者无意中创建了与现有标识符冲突的成员名称。
- 它可用于模拟返回类型的协方差。
关于第一点……基类的作者以后可能会添加一个与派生类中现有成员同名的成员。基类作者可能不了解派生类,因此不期望她应该避免名称冲突。C# 使用隐藏机制支持类层次结构的独立演化。
关于第二点......您可能希望一个类实现一个接口,该接口指示某个方法签名,因此您只能返回某种类型的实例,同时您已经对该类型进行了子类化并且真的很喜欢调用者改为查看具体类型。考虑这个例子。
public interface IFoo { }
public class ConcreteFoo { }
public abstract class Base
{
private IFoo m_Foo;
public Base(IFoo x) { m_Foo = x; }
public IFoo Foo { get { return m_Foo; } }
}
public class Derived
{
public Derived(ConcreteFoo x) : base(x) { }
public new ConcreteFoo Foo { get { return (ConcreteFoo)base.Foo; } }
}