I have just learned how to mask a base class member (using new) but am missing the point as开发者_开发百科 to why I would want to do that. Does masking provide us with a certain level of protection as is the case in using encapsulation? Please advise.
You will very rarely use "new" to mask a base class member.
It's mainly used for the cases where the derived class had the member first, and then it was added to the base class --- the same name for a different purpose. The new
is there to that you acknowledge that you know you are using it differently. When a base member is added in C++, it just silently merges the existing method into the inheritance chain. In C#, you will have to choose between new
and override
, to show you know what is happening.
It's not just used for masking. It actually breaks the inheritance chain, so if you call the base class method, the method in the derived class will not be called (just the one in the base class).
You're essentially creating a new method that has nothing to do with the base class method. Hence the "new" keyword.
Keeping that in mind the "new" keyword can be used if you want to define a method with the same signature as a base type method, but having a different return type.
The only valid safe examples that I've come across is being more specific with return types or providing a set accessor on a property. I'm not saying those are the only ones, but that's all I've found.
For example, suppose you have a very simple base that looks like this:
public abstract class Base
{
public string Name { get; protected set; }
public Base(string name)
{ Name = name; }
}
You could have a derived that looks more like this:
public class Derived : Base
{
public new string Name
{
get { return base.Name; }
set { base.Name = value; }
}
public Derived(string name) : base(name)
{ }
}
Assuming business rules allows this one specific Derived to have a changeable name, I believe this is acceptable. The problem with new
is that it changes behavior depending on what type the instance is viewed as. For example, if I were to say:
Derived d = new Derived("Foo");
d.Name = "Bar";
Base b = d;
b.Name = "Baz"; // <-- No set available.
In this trivial example, we're fine. We are overriding the behavior with new
, but not in a breaking way. Changing return types requires a bit more finesse. Namely, if you use new
to change a return type on a derived type, you shouldn't allow that type to be set by the base. Check out this example:
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; } }
}
If I could set Child
on the Base
class, I could have a casting problem in the Derived
class. Another example:
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
I can't break any business rules by treating all of my Derived
classes as Bases, it's just convenient to not type check and cast.
I have just learned how to mask a base class member (using new)
FYI this feature is usually called "hiding" rather than "masking". I think of "masking" as clearing bits in a bit array.
am missing the point as to why I would want to do that.
Normally you don't want to. For some reasons to use and not use this feature, see my article on the subject from 2008:
http://blogs.msdn.com/b/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx
Does masking provide us with a certain level of protection as is the case in using encapsulation?
No, it does not.
What you are referring to is called Name Hiding. It is mostly a convenience feature. If you are inheriting from a class for which you do not control the source using new
will let you change the behavior of a method even if it wasn't declared as virtual (or completely change the signature if it is virtual). The new
keyword simply suppresses a compiler warning. You are basically informing the compiler that you are intentionally hiding the method from a parent class.
Delphi had the reintroduce
keyword for the same reason.
What does this buy you other than a suppressed warning? Not a whole lot. You can't access the new
method from a parent class. You can access it from an interface if your child class directly implements the interface (as apposed to inheriting it from its parent class). You can still call the parent class' member from the child. Any additional descendants of your class will inherit the new
member rather than the one in the parent.
This is actually called member hiding. There are a couple of common scenarios where this can be appropriately used.
- It allows you to work around versioning issues in which either the base or derived class author unwittingly creates a member name that collides with an existing identifier.
- It can be used to simulate covariance on return types.
Regarding the first point...it is possible that an author of a base class could later add a member with the same name as an exisiting member in a derived class. The base class author may not have an knowledge of the derived classes and thus there is no expectation that she should avoid name collisions. C# supports the independent evolution of class hierarchies using the hiding mechanisms.
Regarding the second point...you may want a class to implement an interface that dictates a certain method signature and so you are locked into returning instances of a certain type only while at the same time you have subclassed that type and would really like for callers to see the concrete type instead. Consider this example.
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; } }
}
精彩评论