tags:

views:

2733

answers:

10

Suppose I have BaseClass with public methods A and B, and I create DerivedClass through inheritance.

e.g.

public DerivedClass : BaseClass {}

Now I want to develop a method C in DerivedClass that uses A and B. Is there a way I can override methods A and B to be private in DerivedClass so that only method C is exposed to someone who wants to use my DerivedClass?

A: 

If they're defined public in the original class, you cannot override them to be private in your derived class. However, you could make the public method throw an exception and implement your own private function.

Edit: Jorge Ferreira is correct.

Cody Brocious
+1  A: 

The only way to do this that I know of is to use a Has-A relationship and only implement the functions you want to expose.

Nescio
A: 

Yes you can. It's called method hiding. However if your caller has a handle to DerivedClass as an instance of BaseClass, they will still be able to call MethodA on that instance.

public DerivedClass:BaseClass 
{
  private new void MethodA()
  {
    //now methodA "hides" the baseClass MethodA and makes it private
  }
}
Mike Brown
+2  A: 

@Brian R. Bondy pointed me to an interesting article on Hiding through inheritance and the new keyword.

http://msdn.microsoft.com/en-us/library/aa691135(VS.71).aspx

So as workaround I would suggest:

class BaseClass
{
    public void A()
    {
        Console.WriteLine("BaseClass.A");
    }

    public void B()
    {
        Console.WriteLine("BaseClass.B");
    }
}

class DerivedClass : BaseClass
{
    new public void A()
    {
        throw new NotSupportedException();
    }

    new public void B()
    {
        throw new NotSupportedException();
    }

    public void C()
    {
        base.A();
        base.B();
    }
}

This way code like this will throw a NotSupportedException:

    DerivedClass d = new DerivedClass();
    d.A();
smink
If you use DerivedClass d = new DerivedClass(); d.A() you will still be able to access the base class' A() implementation.
Brian R. Bondy
Jorge, I just tried this and as Brian pointed out, A and B would still be exposed.
Lyndon
http://msdn.microsoft.com/en-us/library/aa691135(VS.71).aspx
Brian R. Bondy
I think this workaround could work.
smink
+5  A: 

That sounds like a bad idea. Liskov would not be impressed.

If you don't want consumers of DerivedClass to be able to access methods DeriveClass.A() and DerivedClass.B() I would suggest that DerivedClass should implement some public interface IWhateverMethodCIsAbout and the consumers of DerivedClass should actually be talking to IWhateverMethodCIsAbout and know nothing about the implementation of BaseClass or DerivedClass at all.

Hamish Smith
I did a little research into interfaces and maybe I'm missing something big here, but if I defined the interface, I'd want to implement it using methods A and B anyway. A and B are helper functions to implement larger functions in the derived classes.
Lyndon
Lyndon, hopefully the code in the accepted answer makes it clear that DerivedClass is still a subclass of BaseClass and you can still use function A and B in DerivedClass to implement C but the outside world doesn't get to interact with DerivedClass as a DerivedClass and uses the interface instead
Hamish Smith
+21  A: 

It's not possible, why?

In C#, it is forced upon you that if you inherit public methods, you must make them public. Otherwise they expect you not to derive from the class in the first place.

Instead of using the is-a relationship, you would have to use the has-a relationship.

The language designers don't allow this on purpose so that you use inheritance more properly.

For example one might accidentally confuse a class Car to derive from a class Engine to get it's functionality. But an Engine is functionality that is used by the car. So you would want to use the has-a relationship. The user of the Car does not want to have access to the interface of the Engine. And the Car itself should not confuse the Engine's methods with it's own. Nor Car's future derivations.

So they don't allow it to protect you from bad inheritence hierarchies.

What should you do instead?

Instead you should implement interfaces. This leaves you free to have functionality using the has-a relationship.

Other languages:

In C++ you simply specify a modifier before the base class of private, public or protected. This makes all members of the base that were public to that specified access level. It seems silly to me that you can't do the same in C#.

The restructured code:

interface I
{
    void C();
}

class BaseClass
{
    public void A() { MessageBox.Show("A"); }
    public void B() { MessageBox.Show("B"); }
}

class Derived : I
{
    public void C()
    {
        b.A();
        b.B();
    }

    private BaseClass b;
}

I understand the names of the above classes are a little moot :)

Other suggestions:

Others have suggested to make A() and B() public and throw exceptions. But this doesn't make a friendly class for people to use and it doesn't really make sense.

Brian R. Bondy
Nicely written answer.
Jay Bazuzi
Interfaces are definitely one recommended way to go, but it shouldn't be the only step. You still have to be aware of if your code is violating the Single Responsibility Principle or not. Even implementing Interfaces doesn't prevent you from writing monolithic classes that are large and unwieldy.
jolson
+1  A: 

Hiding is a pretty slippery slope. The main issues, IMO, are:

  • It's dependent upon the design-time declaration type of the instance, meaning if you do something like BaseClass obj = new SubClass(), then call obj.A(), hiding is defeated. BaseClass.A() will be executed.

  • Hiding can very easily obscure behavior (or behavior changes) in the base type. This is obviously less of a concern when you own both sides of the equation, or if calling 'base.xxx' is part of your sub-member.

  • If you actually do own both sides of the base/sub-class equation, then you should be able to devise a more manageable solution than institutionalized hiding/shadowing.
Jared
A: 

I would say that if you have a codebase that you are wanting to do this with, it is not the best designed code base. It's typically a sign of a class in one level of the heirarchy needing a certain public signature while another class derived from that class doesn't need it.

An upcoming coding paradigm is called "Composition over Inheritance." This plays directly off of the principles of object-oriented development (especially the Single Responsibility Principle and Open/Closed Principle).

Unfortunately, the way a lot of us developers were taught object-orientation, we have formed a habit of immediately thinking about inheritance instead of composition. We tend to have larger classes that have many different responsibilities simply because they might be contained with the same "Real World" object. This can lead to class hierarchies that are 5+ levels deep.

An unfortunate side-effect that developers don't normally think about when dealing with inheritance is that inheritance forms one of the strongest forms of dependencies that you can ever introduce into your code. Your derived class is now strongly dependant on the class it was inherited from. This can make your code more brittle in the long run and lead to confounding problems where changing a certain behavior in a base class breaks derived classes in obscure ways.

One way to break your code up is through interfaces like mentioned in another answer. This is a smart thing to do anyways as you want a class's external dependencies to bind to abstractions, not concrete/derived types. This allows you to change the implementation without changing the interface, all without effecting a line of code in your dependent class.

I would much rather than maintain a system with hundreds/thousands/even more classes that are all small and loosely-coupled, than deal with a system that makes heavy use of polymorphism/inheritance and has fewer classes that are more tightly coupled.

Perhaps the best resource out there on object-oriented development is Robert C. Martin's book, Agile Software Development, Principles, Patterns, and Practices.

jolson
+3  A: 

What you need is composition not inheritance.

class Plane
{
  public Fly() { .. }
  public string GetPilot() {...}
}

Now if you need a special kind of Plane, such as one that has PairOfWings = 2 but otherwise does everything a plane can.. You inherit plane. By this you declare that your derivation meets the contract of the base class and can be substituted without blinking wherever a base class is expected. e.g. LogFlight(Plane) would continue to work with a BiPlane instance.

However if you just need the Fly behavior for a new Bird you want to create and are not willing to support the complete base class contract, you compose instead. In this case, refactor the behavior of methods to reuse into a new type Flight. Now create and hold references to this class in both Plane and Bird. You don't inherit because the Bird does not support the complete base class contract... ( e.g. it cannot provide GetPilot() ).

For the same reason, you cannot reduce the visibility of base class methods when you override.. you can override and make a base private method public in the derivation but not vice versa. e.g. In this example, if I derive a type of Plane "BadPlane" and then override and "Hide" GetPilot() - make it private; a client method LogFlight(Plane p) will work for most Planes but will blow up for "BadPlane" if the implementation of LogFlight happens to need/call GetPilot(). Since all derivations of a base class are expected to be 'substitutable' wherever a base class param is expected, this has to be disallowed.

Gishu
A: 

When you, for instance, try to inherit from a List, and you want to hide the direct Add(object _ob) member:

    // the only way to hide
    [Obsolete("This is not supported in this class.", true)]
    public new void Add(object _ob)
    {
        throw NotImplementedException("Don't use!!");
    }

It's not really the most preferrable soultion, but it does the job. Intellisense still accepts, but at compiletime you get an error: error CS0619: 'TestConsole.TestClass.Add(TestConsole.TestObject)' is obsolete: 'This is not supported in this class.'

nono