52

I have a common assembly/project that has an abstract base class, then several derived classes that I want to make public to other assemblies.

I don't want the abstract base class to show up in these other assemblies in Intellisense, so I thought I'd make it internal, but I get this error:

Inconsistent accessibility: base class 'Settings' is less accessible than class 'IrcSettings' ....

I don't really get this. I am forced to make the abstract Settings class public, and thus visible outside this assembly.

How can I make this class internal instead?

Hannele
  • 9,301
  • 6
  • 48
  • 68
m3ntat
  • 3,635
  • 11
  • 38
  • 50
  • Just a small point, you can have interfaces as base type which is internal to assembly, but its members (of the implementation) will be public (available outside assembly). If abstract class doesn't have any implementation and members are all public, then one should prefer interface. – nawfal May 19 '13 at 19:27
  • Consider refactoring your abstract base class to a regular private class, then keep an instance of that class in a private field. *(Composition over inheritance.)* – mindplay.dk Apr 15 '20 at 12:41

7 Answers7

102

As I understand, you want your abstract class to only be implemented by other classes in the same assembly (e.g. it is internal) but the derived classes could be public.

The way to do this is to make the abstract base class public, but give it an internal default constructor:

public abstract class MyClass
{
    internal MyClass() { }
}

This will allow MyClass (and hence its members) to be visible and usable to classes outside your assembly, but classes outside your assembly cannot inherit from it (will get a compile error).

Edit: If classes which can be seen by external assemblies inherit from MyClass, you cannot prevent MyClass from also being seen - e.g., showing up in Intellisense. However, you can prevent them from being used by following the above.

Rex M
  • 142,167
  • 33
  • 283
  • 313
  • 3
    My question is not about missue. I've already prevented that. It's about not polluting the Intellisense with a Class that is unusable outside the assembly. – m3ntat Aug 07 '09 at 14:25
  • 2
    You _cannot_ make base classes less accessible than derived classes. You _can_ make the base class members (but not the class itself) internal instead. – thecoop Aug 07 '09 at 15:02
10

The abstract base class has to be public, as the entire inheritance heirarchy for a class has to be visible. This ensures the polymorphism works and is valid; however all the base classes' members can be internal (including the constructor), and hence not usable outside your assembly

thecoop
  • 45,220
  • 19
  • 132
  • 189
  • 3
    Unless I'm mistaken, this has nothing to do with polymorphism: A class can implement an internal interface, and turning that interface into an abstract class changes nothing in to way the methods are inherited etc., for that matter. Theoretically, one could perfecly well define a class which has an internal base class, and use it in other assemblies. It's just that C# (or .NET ?) doesn't allow that. – Suzanne Soy Jun 25 '13 at 15:57
8

There really isn't much of a benefit to what you're trying to achieve but what you're actually looking to achieve is similar to this.

Have your abstract base class in 1 assembly with everything internal. In the AssemblyInfo for that assembly you need to add

[assembly:InternalsVisibleTo("cs_friend_assemblies_2")]

Then in another assembly you have all the classes you want publicly available. Note you will still be able to access the base class from intellisense for any code inside cs_friend_assemblies_2 or whatever you name your assembly but not any where else.

Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
  • 2
    i think this is the only suggestion for the OP to ACTUALLY achieve what he wants although this is a pretty awful method, but still +1 – Letseatlunch May 14 '11 at 03:53
  • 2
    I might not find value in what the OP wants to achieve but I wouldn't say this is an awful approach. This basically opens up "friend assemblies" in .NET. Friend assemblies used in wrong situations can lead to awful code and awful frameworks but that would be from bad practices. I find the most needed use of friend assemblies is for test projects and sometimes in ServiceLayer dependencies (ex UpdatedBy is { public get; internal protected set } and the Service is allowed to assign UpdatedBy but no where else is). – Chris Marisic May 16 '11 at 12:50
  • 1
    i came across this question because i had a similar need to the OP and found yours to be the only that would actually work for the OP. In my case, however, it would Not work for certain reasons and this was not an option. So i wouldn't call this an "Awful" solution either, that IS an exaggeration but it did feel this is more like a shechy work around that won't work in all cases. – Letseatlunch May 16 '11 at 16:54
5

You can't simultaneously make the class available to other assemblies for inheritance but also private so it can't be visible to other consumers. You can make the class internal, and expose it to a specific assembly (if it's a friend assembly) using the [InternalsVisibleTo] attribute, but I don't think this is what you want.

If you want to keep code (other than derived classes) from being able to instantiate your base class, you could give it a protected constructor:

abstract class MyBaseClass
{
    protected MyBaseClass() { ... } // only inheritors can access this...
}

You can hide the class members from Intellisense using the EditorBrowsable attribute:

abstract class MyBaseClass
{ 
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    public void SomeMethodToBeHidden() { }
}

It should be noted that some people have reported problems with the IDE not always respecting this attribute.

LBushkin
  • 129,300
  • 32
  • 216
  • 265
  • 2
    EditorBrowsableAttribute is only valid on class members, not class declarations. – Rex M Aug 07 '09 at 14:22
  • This is what I am looking for, but it doesn't seem to respect this in Intellisense I just tried. – m3ntat Aug 07 '09 at 14:24
  • Also, marking the class abstract prevents it from being instantiated. Having a protected default constructor does not. – Rex M Aug 07 '09 at 14:24
  • The OP indicated that he has an abstract class - my examples simply reflect that case. – LBushkin Aug 07 '09 at 14:40
  • 3
    @LBushkin that's fine, but having a protected default constructor on an abstract class does not actually do anything - the class already cannot be instantiated because it's abstract and the accessibility of the constructor is already restricted to deriving classes by that fact. – Rex M Aug 07 '09 at 14:45
1

As far as I'm concerned, this is a non-problem. Observe:

public abstract class Foo {
    public void virtual Bar() {
        // default implementation
    }
}

public class NormalFoo : Foo { }

public class SpecialFoo : Foo {
    public override void Bar() {
        // special implementation
    }
}

var foolist = new List<Foo>();

foolist.Add( new NormalFoo() );
foolist.Add( new SpecialFoo() );

foreach (var f in foolist) {
    f.Bar();
}

The above wouldn't work at all without polymorphism -- being able to refer to instances of different derived classes through their common interface, the abstract base class. What you want to do is take that away and cripple the usability of your class hierarchy. I don't think you should continue down this path.

Joel B Fant
  • 24,406
  • 4
  • 66
  • 67
0

Will the other assemblies ever inherit from your abstract base class or any of the public classes that do inherit from your abstract base class?

If so, you have to make the abstract base class public. Just make methods you don't want visible outside the assembly internal.

If not, maybe interfaces can help? Define public interfaces, make your public classes implement them, and provide a factory to get instances. That way the only thing intellisense sees outside the assembly is the interface.

Does that help?

n8wrl
  • 19,439
  • 4
  • 63
  • 103
0

A way to work around this limitation is to use composition instead of inheritance (there are other good reasons to do this too). For example, instead of:

internal abstract class MyBase
{
    public virtual void F() {}
    public void G() {}
}

public class MyClass : MyBase // error; inconsistent accessibility
{
    public override void F() { base.F(); /* ... */ }
}

Do this:

public interface IMyBase
{
    void F();
}

internal sealed class MyBase2 : IMyBase
{
    public void F() {}
    public void G() {}
}

public sealed class MyClass2 : IMyBase
{
    private readonly MyBase2 _decorated = new MyBase2();
    public void F() { _decorated.F(); /* ... */ }
    public void G() { _decorated.G(); }
}

You can omit the IMyBase interface entirely if the public doesn't need to know about it and your internals don't either.

Community
  • 1
  • 1
dlf
  • 9,045
  • 4
  • 32
  • 58