21

The essence of the problem is, given a class hierarchy like this:

class A
{
    protected void MethodToExpose()
    {}

    protected void MethodToHide(object param)
    {}
}

class B : A
{
    new private void MethodToHide(object param)
    {}

    protected void NewMethodInB()
    {}
}

class C : B
{
    public void DoSomething()
    {
        base.MethodToHide("the parameter"); // This still calls A.MethodToHide()
        base.MethodToExpose(); // This calls A.MethodToExpose(), but that's ok
        base.NewMethodInB();
    }
}

How can I prevent any classes that inherit from class "B" from seeing the method A.MethodToHide()? In C++, this was easy enough by using a declaration such as class B : private A, but this syntax is not valid in C#.

For those interested (or wondering what I'm really trying to do), what we're trying to do is create a wrapper for for Rhino.Commons.NHRepository that hides the methods we don't want to expose to our group of developers, so we can have a cookie-cutter way of developing our app that new developers can easily follow. So yes, I believe the "Is-A" test is valid for the whole chain (WidgetRepository Is-A BaseRepository Is-A NHRepository).

Edit: I should have mentioned, for the sake of argument, that class A is an API class outside of our control. Otherwise the problem gets considerably easier.

Ogre Psalm33
  • 21,366
  • 16
  • 74
  • 92

10 Answers10

23

Obsolete It

In class B, override MethodToHide and add the Obsolete attribute

[Obsolete("Reason", true)] // true will cause a compile-time error

Set EditorBrowsable

(As mentioned previously)

In class B, override MethodToHide and add the EditorBrowsable attribute

[System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)]

Throw exception

(As mentioned previously)

In class B, override MethodToHide and throw exception.

Create Wrapper

I think Michael Meadows is right. Use the Adapter pattern. This pattern also allows easier mocking of code when unit testing.

class B: IInterface
{    
    protected void MethodToExpose()
    {
        A a = new A();
        a.MethodToExpose();
    }

    protected void NewMethodInB()
    {
    }
}
Monkey Code
  • 592
  • 6
  • 20
  • 1
    Interesting approach! I'm surprised nobody has mentioned the Obsolete attribute yet. Probably not quite the original intent of the attribute, but never the less, I think appropriate. – Ogre Psalm33 Jan 17 '13 at 13:26
  • How about adding a new member of a completely different kind? Perhaps an empty public static class? I'm not sure exactly how all .NET languages handle shadowing, but I think the new class will hide all base-class members with the same name. – supercat Jul 27 '13 at 16:18
  • Adding the `Obsolete` attribute sounded like a good idea, but unfortunately it will throw [a compiler warning](http://msdn.microsoft.com/en-us/library/bb397626(v=vs.90).aspx). The error property will also have no effect. – David Sherret Dec 10 '13 at 14:17
  • @DavidSherret If I am correct this should remove the warning: #pragma warning disable 0809 – Monkey Code Dec 08 '14 at 10:40
  • 1
    Better than accepted answer – nyconing Feb 07 '18 at 03:10
21

You can't do it and preserve the hierarchy. If possible, you should create interfaces that define your ideal, then subclass the bases classes and implement the interfaces. reference the interfaces only (not the base class types) in your code.

The Adapter pattern was created specifically to solve the problem of how to use a framework when its API doesn't line up exactly with your needs.

John Rasch
  • 62,489
  • 19
  • 106
  • 139
Michael Meadows
  • 27,796
  • 4
  • 47
  • 63
  • +1: I like this solution! As long as developers stick with the interface and don't explicitly cast it to the custom class, they won't have access to anything but what is specified in the interface. – Blixt Jul 14 '09 at 14:35
  • 1
    That's a slick idea. This is along the lines of what we're already doing (using NHibernate, MVC, Dependency Injection, ...), so we already have interfaces for the repositories (class C). That keeps the clients of Class C in the dark about what's under-the-covers. All that's left to worry about is how C is implemented. In our case, there will be many "C"-level child classes (repositories for different objects). So our ideal is probably having B be a wrapper/adapter to A. Unfortunately, A has a lot of methods to decide if we want to hide or expose in B. – Ogre Psalm33 Jul 14 '09 at 16:00
14

If you want to custom tailor a set of features, I'd say you want to make a wrapper rather than inherit the functionality. I don't know of a way to do what you want in C# either.

Think about it, what if some code outside your control wants this NHRepository instance for something... but you've removed the functionality from the function it needs in your child class (which you would send in, since that's your only NHRepository instance.) Everything goes boom. That's why I don't think it's even possible to do without some ugly hack.

Blixt
  • 49,547
  • 13
  • 120
  • 153
  • 3
    +1 I'd bet on doing a wrapper as well, rather than inheritance. Instead of focusing on what you want to hide, you can focus on what you want to expose. – Will Eddins Jul 14 '09 at 14:34
  • I think the closest fit would be to override or declare new methods in the subclass that throw NotSupportedExceptions--definately ugly! – STW Jul 14 '09 at 14:36
  • @Yoooder: I doubt all the methods are `virtual` so they can't be overridden (and overriding could mess up the behavior of the whole class anyways) and attempting to hide them with `new` appears to be something he already tried (see his example.) – Blixt Jul 14 '09 at 14:39
6

If you are using Visual Studio, you can hide the methods/properties from intelliprompt with this attribute:

class A
{
    protected void MethodToExpose()
    {}

    [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)]
    protected void MethodToHide(object param)
    {}
}

It won't actually get rid of the function, but if they are just your internal people. It might be close enough.

Jake Pearson
  • 27,069
  • 12
  • 75
  • 95
  • 1
    I forgot to mention when I first posted that "class A" is an API class outside of our control. So modifying class A is out. However, I'll play around with this. [System.ComponentModel.EditorBrowsable(...)] appears to be a handy feature that may come in useful at some point! – Ogre Psalm33 Jul 14 '09 at 15:51
  • 1
    You could probably add on the attribute down in class B. – Jake Pearson Jul 14 '09 at 16:51
  • 1
    I'm not sure if this is an idea that should even be considered for a second. It will inevitably cause confusion, which will add to technical debt. – SideFX Nov 15 '10 at 19:35
4

In my opinion, the best way to do it, if you just want to hide and not really override and externalize a new functionallity, is just to add an EditorBrowsableState attribute. It hides the the method in the visual studio editor. Who ever will try to use it will end up with a compile time error.

Just add this code on top of your method:

[System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)]

For example

public class User
{        
    public void DoSomething()
    {
       Something...
    }
}

public class Manager
{
    [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)]
    public override void DoSomething()
    { }
}

void main()
{
   User user = new User();
   user.DoSomething();

   Manager manager = new Manager();
   manager.DoSomething(); // --------- This row will throw a design time error
}

Good Luck :)

Guy Levin
  • 1,230
  • 1
  • 10
  • 22
  • `[System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)]` does **not** throw a design time error in LINQPad. Even more so: it **is** shown by Intellisense. Haven't checked with Visual Studio, still this is clearly not the way to go if the code is intended for anyone else than yourself. – Robert Synoradzki Nov 18 '17 at 22:59
1

C# does not have a concept similar to protected or private inheritance in C++.

Your best option is to aggregate an instance of the class and expose the set of methods you are interested in you consumers having access to.

Although in your case I don't think it's possible, you could look into creating an interface that exposes just the common functionality that you want consumers be work with so that your wrapper can be substitutable in some instances for it's aggregate.

LBushkin
  • 129,300
  • 32
  • 216
  • 265
1

I never knew you could do that in C++ though I don't know much about C++. I'm afraid that I agree with Blixt that a wrapper class is probably how I'd implement this.

It might work (but I'm not sure) to simply override the function and throw an exception upon a call....

Frank V
  • 25,141
  • 34
  • 106
  • 144
  • This does work. My fellow developer is leaning towards this solution, but I'm not satisfied because it doesn't seem that clean. – Ogre Psalm33 Jul 14 '09 at 15:47
  • 1
    C++ is the definitive "you can do anything you want" language, with a lot of surprising flexibility. Including the flexibility to shoot yourself in the foot, hang yourself, run yourself over with a bus... – Ogre Psalm33 Jul 14 '09 at 16:04
1

As far as I know you can't hide it in the way you want. I think you could prevent it from being used in any practical manner though:

class B : A
{
    public new void MethodToHide(object param)
    { 
        throw new DontUseThisMethodException();
    }

    protected void NewMethodInB()
    {}
}

Not the nicest thing to do though, so you would probably want to solve it in some other way...

Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • 2
    My coworker decided to use this approach. It's nicer to hide it at compile time instead of run-time, though. – Ogre Psalm33 Feb 22 '10 at 18:13
  • 1
    it a NO NO to have throw new notimplementedException or some sort.. it violates the Interface Segregation Principle – user384080 Nov 26 '13 at 05:42
1

Actually you CAN hide A's method from C if you would have defined B's method as accessible from C.

The only problem with your code is that you use "private" on your hiding declaration... if you use protected or public you would not have any issues and it would operate as you expect. I do this all the time with fields.

KDogg
  • 11
  • 1
  • I think I see what you're trying to say, but I really don't want class C to see any version of MethodToHide(param), whether it's inherited from B or A. A coworker's solution is along the lines of what you suggested, though. He overrides MethodToHide(param) in B, but has the extra step of making B.MethodToHide(param) throw a NotImplementedException. Similar to Fredrick's solution. – Ogre Psalm33 Feb 22 '10 at 18:12
  • @OgrePsalm33: Create some other member with that name and in incompatible signature--like maybe a write-only property whose type is an `enum` called `DontEvenThinkAboutIt`, whose only defined member is `ItWontWork`. – supercat Jul 27 '13 at 13:57
0

Your code in derived class B won't hide the base method from all derived types, only itself. You would have to set the method to private in the base. The only way around this issue is to create another base class that does not expose this method.

James
  • 80,725
  • 18
  • 167
  • 237