Reputation: 8316
I have a base class Foo that is concrete and contains 30 methods which are relevant to its subclasses.
Now I've come across a situation that is specific only to the base class,and I want to create a method that cannot be inherited, is this possible?
Class Foo
{
/* ... inheritable methods ... */
/* non-inheritable method */
public bool FooSpecificMethod()
{
return true;
}
}
Class Bar : Foo
{
/* Bar specific methods */
}
var bar = new Bar();
bar.FooSpecificMethod(); /* is there any way to get this to throw compiler error */
EDIT
I'm not sure if I was clear originally.
I do understand the principles of inheritance, and I understand the Liskov substitution principle. In this case there is a single exception that ONLY deals with the 'un-inherited' case, and so I did not want to create an 'uninheritedFoo' subclass.
I was asking if it is technically possible to create a situation where foo.FooSpecificMethod() is a valid and publicly accessible method, but subclassoffoo.FooSpecificMethod() throws a compiler error.
Essentially I want a sealed method on an unsealed class.
Upvotes: 6
Views: 12270
Reputation: 56
I have been trying to achieve this myself and came across this post which has some great answers without achieving what the poster was after, but there is a way to enforce this. You need to override the existing method and then use the Obsolete attribute. Setting the Obsolete attribute's error property to true will prevent compile.
Class Foo
{
/* ... inheritable methods ... */
/* non-inheritable method */
public bool FooSpecificMethod()
{
return true;
}
}
Class Bar : Foo
{
/* Bar specific methods */
[Obsolete("Don't use this for whatever reason", true)]//true triggers the compiler error
public new bool FooSpecificMethod()
{
return true;
}
}
var bar = new Bar();
bar.FooSpecificMethod(); /* this now throws a compiler error */
Upvotes: 1
Reputation: 91
Perhaps you could simply create it as a virtual method on the parent class and then override it, throwing a NotImplementedException or something similar, on the child class that inherited it.
Also, but only if you're willing to put up with some spaghetti, you could put the method's signature in an interface and then have all the classes that'll require such method implement it. The downside is you'll have to declare the same method in all those children.
I'm not sure if it's even possible, but maybe an extension method could be written for that interface?
Keep in mind that these are all violations of OOP's encapsulation principle, albeit minor, which makes them all the more fun.
Upvotes: 0
Reputation: 1192
As others have pointed out, this is not possible, and most importantly, it shouldn't be necessary.
I came across the same problem when I had the following structure:
- A parent class with properties of child classes.
- The child classes would expose different properties, but all had the same initialisation procedure, so I put the code in the parent class.
public class Parent
{
public CHILD1 Child1;
public CHILD2 Child2;
public Parent(params args){
Child1 = new CHILD1(arg1, arg2);
Child2 = new CHILD2(arg1, arg2);
}
//split here
public Parent(object arg1, object arg2) {
PropertyInfo[] list = this.GetType().GetProperties();
foreach (var pi in list) {
pi.SetValue(this, BL.Method(arg1, arg2, this.GetType().Name, pi.Name) // get data
}
}
}
public class CHILD1 : Parent
{
public CHILD1(object arg1, object arg2) : base(arg1, arg2) { }
public object C1Property1 { get; set; }
public object C1Property2 { get; set; }
// ..
}
public class CHILD2 : Parent
{
public CHILD2(object arg1, object arg2) : base(arg1, arg2) { }
public object C2Property1 { get; set; }
public object C2Property2 { get; set; }
// ..
}
Now I could call parent.Child1.C1Property1
. But the code would also allow a call of the following: parent.Child1.Child1.Property1
, which wouldn't make sense and result in an exception because the Child1.Child1
property would always be null
.
In order to fix this, all I had to do was split the code of the parent class into two classes:
Upvotes: 0
Reputation: 35171
Brian's right about Liskov Substitution (upmodded). And Reed's right about "is-a" (also upmodded); in fact they're both telling you the same thing.
Your public methods are a contract with users of your class Foo, saying that, "you can always" call those methods on Foo.
Subclassing Foo means that you're saying that a Subclass, e.g. Bar, is always acceptable to use where a Foo could be used. In particular, it means you inherit not (necessarily) Foo's implementation (you can override that, or Foo may be abstract and give no particular implementation for a method).
Inheritance of implementation (if any) is a detail; what you're really inheriting is the public interface, the contract, the promise to users that a Bar can be used like a Foo.
Indeed, they may never even know they have a Bar, not a Foo: if I make a FooFactory, and I write its Foo* getAFoo() to return a pointer to a Bar, they may never know, and sholdn't have to.
When you break that contract, you break Object Orientation. (And the Java Collection classes, by throwing NotSupported exceptions, entirely break OO -- users can no longer use so-called subclasses polymorphically. That's poor, poor design, which has caused major headaches for many many Java users, not something to emulate.)
If there's a public method that Foo subclasses can't use, then that method shouldn't be in Foo, it should be in a Foo subclass, and the other subclasses should derive from Foo.
Now, that does NOT mean that all methods in Foo should be callable on subclasses. No, I'm not contradicting myself. Non-public methods are not part of a class's public interface.
If you want a method in Foo that can't be called on Bar, and shouldn't be publicly callable in Foo, then make that method private or protected.
Upvotes: 6
Reputation: 12087
You should consider Composition [http://en.wikipedia.org/wiki/Composite_pattern} over inheritance if you want to achieve what you are trying to do and expose methods from the private object (i.e. the one you are currently inheriting from).
Upvotes: 0
Reputation: 81217
There are two reasons a derived class may hide base-class members without violating the Liskov Substitution Principle:
I would posit that while base-class member hiding is often a sign of bad design, there are cases where hiding base-class members would be more appropriate than leaving them exposed; if a derived class cannot possibly use a protected method correctly (as would be the case with the MemberwiseClone
method available to derivatives of List<T>
) there's no reason to have it exposed. Unfortunately, there's no completely clean way of hiding such members. The best one can do is declare a public member of the same name (using a "new" modifier) which would block off all access to the base-class member. For example, code could define a nested public static class named MemberwiseClone
with no members. The EditorBrowseable
attribute could be used to prevent Intellisense from showing that nested class, but even if code did try to use that class, it has no members, and one cannot create a storage location of that type, so it should be pretty harmless.
Upvotes: 0
Reputation: 8316
The solution I ended up using was creating a public inner inherited class. That way it is able to access the private variables of the baseclass (as it needs to) and I don't need to expose publicly any of those private variables or functions (which I don't want to).
Thanks so much for all your advice, it caused me to rethink exactly what I needed from this arrangement.
Upvotes: 0
Reputation: 564631
I would rethink the need for this.
If you are using inheritance, you are suggesting that "Bar" IS A "Foo". If "Bar" is always a "Foo", methods that work on "Foo" should also work on "Bar".
If this isn't the case, I would rework this as a private method. Publically, Bar should always be a Foo.
Just to take this one step further -
If you could do this, things would get very complicated. You could have situations where:
Foo myBar = new Bar(); // This is legal
myBar.FooSpecificMethod(); // What should this do?
// It's declared a Foo, but is acutally a Bar
You can actually force this behavior using reflection, though. I think it's a bad idea, but FooSpecificMethod() could check the type of this, and if it isn't typeof(Foo), throw an exception. This would be very confusing, and have a very bad smell.
Edit in response to question's edit:
There is no way for the compiler to enforce what you are asking. If you really want to force the compiler to check this, and prevent this, you really should consider making Foo a sealed class. You could use other extension methods than subclassing in this case.
For example, you might want to consider using events or delegates to extend the behavior instead of allowing the object to be subclasses.
Trying to do what you are accomplishing is basically trying to prevent the main goals of inheritance.
Upvotes: 11
Reputation: 6636
Making the function private will prevent it from being called by the subclasses directly.
If you are talking about virtual functions that you do not want to be overloaded, marking the function as sealed at the point that you want to "lock" the function works.
Even if it is a private function, it could still be called by reflection.
You could also declare the function on an interface, and explicitly implement the interface on the class, which would force you to cast it to the interface to use the function.
Upvotes: 2
Reputation: 66132
You could make a private function, and then call it using reflection. Probably a little overboard. Anyway, just put the function in your base class, along with comments saying it should only be called from the base class. Maybe even those nice /// comments that show up with intellisense. Then, you might get a bug, but then well, you'll always get bugs, and the best you can do is document this situation to try and avoid it.
Upvotes: 1
Reputation: 118895
No, this would violate the Liskov substitution principle.
Pragmatically, you can either have it "throw NotImplementedException()" in Bar, or remove the method from Foo and move it down to the subclasses to which it applies.
Upvotes: 4