21

I've been using c# since version 1, and have never seen a worthwhile use of member hiding. Do you know of any?

James L
  • 16,456
  • 10
  • 53
  • 70

7 Answers7

16

Scenario #1:

Imagine you are designing the runtime library for .NET 2.0. You now have generics at your disposal. You have an interface:

interface IEnumerable 
{
    IEnumerator GetEnumerator();
}

You wish to make a new interface

interface IEnumerable<T> 
{
    IEnumerator<T> GetEnumerator();
}

You now have three choices.

1) Make the generic version unrelated to the non-generic version.

2) Make the generic version extend the non-generic version. You now have two methods that differ only in return type. Change the name of GetEnumerator in the new type to GetEnumerator2(). Because that's hot. Everyone loves a good "2" method.

3) Make the generic version extend the non-generic version. Make the new and improved method hide the existing method so that its there if you need it, but hidden by default.

These are all bad choices. Which would you choose? We chose (3). Good thing that it was an option; without hiding, that option would not have been available.

Now, you might argue that this particular example of hiding was not 'worthwhile'; if that's so, what would you have done instead?

Method hiding makes it possible to expose improved interfaces without causing breakages when there are improvements to the type system.

Scenario #2:

You work at FrobCo. You produce a class Frobber that extends Blobber, which is supplied to you by the good people at BlobCo.

BlobCo has neglected to put a Frobozzle() method on Blobber, but your customers love to frobozzle frobbers, so you add a method Frobozzle() to derived class Frobber.

BlobCo realizes that their customers want to Frobozzle blobbers, so they add a non-virtual method Frobozzle() to Blobber, the base class.

Now what do you do, FrobCo employee?

1) Remove the Frobozzle method on Frobber, thereby breaking your customers who relied on your implementation. Remember, BlobCo doesn't know how to Frobozzle a Frobber; they only wrote code that knows how to Frobozzle a Blobber.

2) Whine to BlobCo that they should have made their method virtual. Hope they do something about it someday.

3) Hide their method in your derived class.

Method hiding helps mitigate the brittle base class problem.

Further Reading:

http://blogs.msdn.com/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • And choice #2.2) might be wrong, particularly if your definition of Frobozzle and BlobCo's definition of Frobozzle are completely different. – Brian May 07 '10 at 19:51
  • don't forget bad choice #4 in scenario #2: Stay on the legacy build of the BlobCo library that doesn't have the Frobozzle method, thereby missing out on any other code updates e.g. security patches, improved performance, new functionality, etc... – danielpops May 01 '11 at 19:49
  • Wow I just couldn't believe for a moment that Queen's guitar player has commented on SO :) – Zoltán Tamási Jan 19 '15 at 13:16
11

Making the return type more explicit - for example SqlConnection.CreateCommand returns a SqlCommand, not a DbCommand. Actually, it looks like this isn't declared new, but that would be one of the few times I would use this; most times it is evil.

Another use: remove inherited attributes from a member.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
5

We had a class that inherited from the WebControl class. When we upgraded from 3.5 to 4.0, the way that some of our attributes (mainly around onclick and javascript) changed quite radically.

We could not change how the AttributeCollection class was handled by the WebControl, so what we did was implement the same methods and properties of the AttributeCollection in our custom collection and hid the AttributeCollection property with our own.

None of the accessing code changed, and we were able to correctly implement how to handle the attributes in our classes. Now no one needs to know what we did to fix the problem. All they need to know is that they call Attributes.Add like normal, and we fix things under the hood.

kemiller2002
  • 113,795
  • 27
  • 197
  • 251
2

When making controls with rich design-time experiences, it's frequently necessary to shadow existing properties to apply attributes.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
2

Generally it's considered bad practice to reject an inherited member. The only time I've used it is in generated code where the new member provided a more specific type in the same class hierarchy. Same goes for explicit interface implementations.

dpurrington
  • 1,488
  • 11
  • 20
1

Sometimes I make a class derived from something in the base class library, where the base class (for performance reasons) did not use a virtual function. In that case it makes sense to use 'new'. Here's one example (which matches what Marc Gravell was talking about), a strongly typed WeakReference:

public class WeakReference<T> : System.WeakReference
{
    public WeakReference(T target) : base(target) { }
    public WeakReference(T target, bool trackResurrection) : base(target, trackResurrection) { }
    #if !WindowsCE
    protected WeakReference(SerializationInfo info, StreamingContext context) : base(info, context) {}
    #endif
    public new T Target
    {
        get { return (T)base.Target; }
        set { base.Target = value; }
    }
}

In cases where the base class method actually IS virtual, I think Microsoft was envisioning cases where the base class is implemented by one party and the derived class is implemented by a second party. The second party adds a function "Foo", then later the first party adds another function "Foo" that actually does something different (so overriding would not be appropriate). Then, in order to maintain compatibility with third party code, the second party keeps their function named "Foo" despite the fact that it is not directly related to the base class version. In that case, the second party adds "new" to their declaration.

Qwertie
  • 16,354
  • 20
  • 105
  • 148
0
  1. When a new version of a Type implements a publicly-visible member whose name conflicts with a name you've used in a derived Type.

  2. To hide an inappropriate implementation in a base class from which you derive.

    For example KeyedCollection<TKey, TItem>.Contains(TItem item) is inherited from Collection<TItem> and is therefore an O(n) operation rather than the O(1) operation that KeyedCollection<TKey, TItem>.Contains(TKey key) usually provides.

    This is arguably confusing as naive consumers may think the two are interchangeable.

    So it may be appropriate to implement a "better" version:

    public new bool Contains(TItem item)
    {
        if (item == null) throw new ArgumentNullException("item");
    
        return this.Contains(GetKeyForItem(item));
    }
    
Joe
  • 122,218
  • 32
  • 205
  • 338