我刚学会了如何掩盖(使用new)基类成员,但是我不明白为什么要这样做。掩盖是否像封装一样为我们提供一定程度的保护?请指导。
我刚学会了如何掩盖(使用new)基类成员,但是我不明白为什么要这样做。掩盖是否像封装一样为我们提供一定程度的保护?请指导。
我遇到的唯一几个有效而且安全的例子是更加明确地指定返回类型或在属性上提供一个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)
{ }
}
假设业务规则允许这个特定的派生类型有可变的名称,我认为这是可以接受的。使用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; } }
}
如果我可以在Base
类上设置Child
,那么在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
类视为基类来违反任何业务规则,这只是不进行类型检查和转换的便利。
它不仅用于掩盖,实际上还会打破继承链,因此如果您调用基类方法,则不会调用派生类中的方法(只有基类中的方法)。
实质上,您正在创建一个与基类方法无关的新方法。因此使用了 "new" 关键字。
记住,如果您想定义具有与基类型方法相同签名但返回类型不同的方法,则可以使用 "new" 关键字。
new
的原因是你知道你正在以不同的方式使用它。当在 C++ 中添加一个基本成员时,它只会将现有方法静默地合并到继承链中。在 C# 中,你必须选择 new
和 override
之间进行选择,以显示你知道发生了什么。http://blogs.msdn.com/b/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx
在编程中,使用掩码是否像封装一样为我们提供了一定程度的保护?
不是的。
你所指的是名称隐藏。这主要是一种方便功能。如果你从一个你无法控制源代码的类继承,使用new
将允许你改变一个方法的行为,即使它没有被声明为虚拟的(或者完全改变签名,如果它是虚拟的)。new
关键字只是抑制了编译器警告。你基本上是在告诉编译器,你有意隐藏了父类的方法。
Delphi也有同样原因的reintroduce
关键字。
除了抑制警告之外,这给你带来了什么好处?并不是很多。你不能从父类访问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; } }
}