Why can't my public class extend an internal class?

21,398

Solution 1

By inheriting from a class, you expose the functionality of the base class through your child.

Since the child class has higher visibility than its parent, you would be exposing members that would otherwise be protected.

You can't violate the protection level of the parent class by implementing a child with higher visibility.

If the base class is really meant to be used by public child classes, then you need to make the parent public as well.

The other option is to keep your "parent" internal, make it non-abstract, and use it to compose your child classes, and use an Interface to force classes to implement the functionality:

public interface ISomething
{
    void HelloWorld();
}

internal class OldParent : ISomething
{
    public void HelloWorld(){ Console.WriteLine("Hello World!"); }
}

public class OldChild : ISomething
{
    OldParent _oldParent = new OldParent();

    public void HelloWorld() { _oldParent.HelloWorld(); }
}

Solution 2

UPDATE: This question was the subject of my blog on November 13th of 2012. See it for some more thoughts on this issue. Thanks for the great question!


You're right; it doesn't have to be that way. Other OO languages allow "private inheritance", whereby the fact that D inherits from B can only be taken advantage of by code that has the ability to see B.

This was a design decision of the original C# designers. Unfortunately I am away from my desk right now - I'm taking a couple of days off for the long weekend - so I don't have the language design notes from 1999 in front of me. If I think of it when I get back I'll browse them and see if there is a justification for this decision.

My personal opinion is that inheritance should be used to represent "is a kind of" relationships; that is, inheritance should represent the semantics of the domain being modelled in the language. I try to avoid situations where inheritance is used as a code sharing mechanism. As others have mentioned, it's probably best to prefer composition to inheritance if what you want to represent is "this class shares implementation mechanisms with other classes".

Solution 3

I think the closest thing you can do is prevent other assemblies creating the abstract class by making its constructor internal, to quote from MSDN:

An internal constructor prevents the abstract class from being used as the base class of types that are not in the same assembly as the abstract class.

You can then try adding an EditorBrowsableAttribute to the class to try and hide it from IntelliSense (though, I've had mixed results using it to be honest) or put the base class in a nested namespace, such as MyLibrary.Internals to seperate it from the rest of your classes.

Solution 4

I think you're mixing concerns here, and C# is to blame, actually (and Java before it).

Inheritance should serve as a categorization mechanism, whereas it's often used for code reuse.

For code reuse it's always been known that composition beats inheritance. The problem with C# is that it gives us such an easy way to inherit:

class MyClass : MyReusedClass { }

But in order to compose, we need to do it by ourselves:

class MyClass {
  MyReusedClass _reused;
  // need to expose all the methods from MyReusedClass and delegate to _reused
}

What's missing is a construct like a trait (pdf), which will bring composition to the same usability level as inheritance.

There's research about traits in C# (pdf), and it would look something like this:

class MyClass {
  uses { MyTrait; }
}

Although I'd like to see another model (that of Perl 6 roles).

UPDATE:

As a side note, the Oxygene language has a feature that lets you delegate all members of an interface to a member property that implements that interface:

type
  MyClass = class(IReusable)
  private
    property Reused : IReusable := new MyReusedClass(); readonly;
      implements public IReusable;
  end;

Here, all interface members of IReusable will be exposed through MyClass and they'll all delegate to the Reused property. There are some problems with this approach, though.

ANOTHER UPDATE:

I've begun implementing this automatic composition concept in C#: take a look at NRoles.

Solution 5

I think this would violate the Liskov Substitution Principle.

In cases like this, I have used internal classes and prefer composition over inheritance. Is there anything about your design that prohibits containing all such functionality in your internal class, and then have your public classes contain an instance of this internal class?

Share:
21,398
David
Author by

David

Updated on July 09, 2022

Comments

  • David
    David almost 2 years

    I really don't get it.

    If the base class is abstract and only intended to be used to provide common functionality to public subclasses defined in the assembly, why shouldn't it be declared internal?

    I don't want the abstract class to be visible to code outside the assembly. I don't want external code to know about it.

  • David
    David almost 14 years
    Thanks. But why shouldn't an internal class's members be considered internal, except when exposed through a public subclass? I think that makes sense.
  • David
    David almost 14 years
    Thank you for your detailed answer and suggestion to use composition. Am I right in thinking that the answer to my question is more 'because C# wasn't created that way' than 'because it would be a stupid thing to do'?
  • Joseph Ferris
    Joseph Ferris almost 14 years
    It is a matter of convention. If you are looking to change the scope of the class through inheritance, you can simply not alter the rules to make something more accessible. Internal specifically should be used to make something accessible to all classes in an assembly. Changing the scope to public violates this, by making a subclass available to other assemblies. A good use of an internal class is protecting core framework concerns, for example. If you insist that your internal class should be accessible outside of the assembly, you likely should not be using the internal modifier.
  • Dave
    Dave almost 14 years
    I prefer the interface approach, but I think David specifically wants to keep such functionality in a "common place" and not have to reimplement the same code in every derived class.
  • Joseph Ferris
    Joseph Ferris almost 14 years
    I don't think it is a matter of "because C# wasn't created that way". Internal is not a limitation, but a feature. It is similar to creating a class in Java with no access modifier, which constrains it to the JAR. It serves a very specific purpose, just one that is not often used (or used often enough, if you will).
  • David
    David almost 14 years
    Okay, I think we can all see that composition is the way to muddle with accessibility of classes. I'm going to just make the base class public because I'm a bad person who can't be bothered to refactor. Thank you all for your enlightening answers.
  • Eric Lippert
    Eric Lippert almost 14 years
    How does it violate the substitution principle? I don't see what you're getting at here.
  • Erik Forbes
    Erik Forbes almost 14 years
    "prefer encapsulation to inheritance" - can you show an example of this? I'm not sure I understand exactly what you mean here, or how you'd use encapsulation to provide shared implementation code.
  • Eric Lippert
    Eric Lippert almost 14 years
    @Erik: I intended to type "prefer composition" and accidentally typed "prefer encapsulation". A rather large typo!
  • David
    David almost 14 years
    I see your point, although chin-stroking on architecture is a luxury for people with a better grip of the language than me! (I mean this in the best possible way, if by some mistake it reads as sarcastic or aggressive.)
  • Dave
    Dave almost 14 years
    @Eric I had it backwards, I guess. You violate the rule if your derived class removes functionality provided by the base, so you wouldn't be able to substitute your base with your derived class. When I first read the question, I jumped to conclusions. That was just a thought at the time -- the main part of my answer still stands IMO -- just use composition instead of inheritance. Thanks for the correction.
  • Erik Forbes
    Erik Forbes almost 14 years
    @Eric - Ah, okay. Glad I'm not totally missing something, lol =) Thanks for clearing it up.
  • James Dunne
    James Dunne almost 14 years
    @Eric: Can these design notes be published? Or would that be a violation of MS policy?
  • Eric Lippert
    Eric Lippert almost 14 years
    @James: The notes are not in a form that is fit for public consumption. We can either spend time debugging the compiler and adding features, or we can spend time cleaning up thousands of pages of design notes mostly about features that were cut a decade ago. The vast majority of our customers would prefer that we spend our budget doing the former activities.
  • David
    David almost 14 years
    I entirely understand your point, but I feel your assertion that 'composition beats inheritance' could be expanded. Can you say anything else to explain why you think so? Thanks.
  • Jordão
    Jordão almost 14 years
    @David: I said that composition beats inheritance for code reuse. You shouldn't have to adhere to an inheritance hierarchy for the sole reason of reusing code. The hierarchy says something about who you are, and that's a very strong relationship (and very precious in a single-inheritance language). For code reuse, you really want to say something about what you do, and composition and delegation allows you to do just that. Interfaces are also good at this, but you still need to implement its members or delegate to someone that does.
  • Anthony Pegram
    Anthony Pegram almost 14 years
    @Eric, public class extending internal base is not permitted, OK. What about interfaces? That seems to be permitted. If memory serves, Tuple<> implements ITuple, which is internal.
  • Eric Lippert
    Eric Lippert almost 14 years
    @Anthony: Good point, I forgot to mention that. Yes, an interface may effectively be a implementation detail of a class.
  • supercat
    supercat over 13 years
    The concept of "inheritance" actually includes two orthogonal concepts: code reuse with default behaviors, and substitutability. Inheritance is appropriate when both are required; composition is better when only code reuse is required. What would be really cool would be generic support for composition of interfaces, so if II is an interface, an object could implement II in terms of a field of that type. Without a generic constraint for interfaces, this obviously won't work, but it would be very handy if Microsoft could add it sometime.
  • Jordão
    Jordão about 13 years
    @supercat: take a look at my first update for a language that has this feature. And take a look at my second update for a feature that's better :-)
  • supercat
    supercat about 13 years
    @Jordão: I'm not totally comfortable with the idea of using post-compilers, though I'm sure people who are comfortable with them find them useful. I suppose if the makers of a primary compiler decline to supply a useful interface one must do what one must do. Still, I think it would be helpful if C#/vb.net would allow a class to "internally" inherit another class but not be considered substitutable to outsiders; the behavior should be as though the "internally inherited" class were duplicated, but hopefully without actually duplicating the code.
  • supercat
    supercat about 13 years
    @Jordão: This could offer an advantage over composition in that creating an instance of the new class would only require creating one object, rather than two. Not always a huge advantage, but sometimes significant. Actually, I've sometimes wished for an ability to apply certain very limited forms of inheritance to classes that were otherwise not inheritable. No new fields, and no overrides of virtual methods, but a means of defining non-virtual methods and also optionally creating a new type in the type system. For example, it might be helpful...
  • supercat
    supercat about 13 years
    ...to define a type WizbangConfigString which would "inherit" string, and define new constructors and maybe some helper methods (conceptually like extension methods, but better scoped). It would be helpful to be able to either define the WizbangConfigString as "really" being a string (implying two-way substitutability) or as being a derived type (implying one-way substitutability). Probably doesn't do too much good to daydream about such features, though.
  • Jordão
    Jordão about 13 years
    @supercat: interesting proposals. Looks like you'd like some form or non-public inheritance in C# (what you have in C++). You could also take a look at implicit conversion operators in C#.
  • supercat
    supercat about 13 years
    @Jordão: I dislike the implementation of extension methods in vb.net/C# since they follow scoping rules totally unrelated to any others. Having extension methods apply only to variables, parameters, and fields which are declared to be of a certain extended type would allow extension methods to take priority over base methods, which would avoid the dangers of code relying upon the non-existence of certain members in the base class. As for adding new types, I've sometimes found it desirable to have a collection where each item was a string plus a tiny bit of info to say what the string was.
  • supercat
    supercat about 13 years
    @Jordão: One could define one or more classes which encapsulate a string, and define implicit an widening conversion to string for those classes, but every object stored in the collection would then require the storage of two objects: the string and the object encapsulating it. Since every object has type information associated with it, it would seem one should be able to make use of that information. To be sure, if one were to go crazy using nested generics so as to store huge amounts of information in object types (theoretically generic types can hold any amount of data) that would...
  • supercat
    supercat about 13 years
    @Jordão: ...severely dog the type system, but I wouldn't expect the type system to mind using an object's type to hold a bit or so worth of information.
  • Zeeshanef
    Zeeshanef over 10 years
    @David: Internal class members are internal because you can not use them outside the assembly. (because internal class will not visible outside the assembly). But if you create a public sub class with base internal class, it will violate the rule, because through the public sub class, internal base class will be visible out side the assembly which will violate the internal class concept.
  • Benedikt M.
    Benedikt M. about 3 years
    In my opinion, this is the actual answer.