How to prevent an abstract class with publicly derived classes from inheriting in other assemblies?

I want to write something like the following:

internal class InternalData { } public class PublicData { } abstract internal class Base { internal Base() { } private static InternalData CreateInternalDataFromPublicData(PublicData publicData) { throw new NotImplementedException(); } abstract protected void DoProcess(InternalData internalData); public void Process(PublicData publicData) { InternalData internalData = CreateInternalDataFromPublicData(publicData); DoProcess(internalData); } } public sealed class Derived : Base { protected override void DoProcess(InternalData internalData) { throw new NotImplementedException(); } } 

That is, Base contains some internal logic and is not inherited by classes outside my assembly; and Derived is available outside. InternalData also contains some internal logic, and since it (and should) will never be used externally, I also want to make it internal.

Of course, the code above will not compile, since Base should not be less accessible than Derived . I can set Base as public , this is fine, but this leads to another problem. If Base is publicly available, some other builds may have multiple ExternalDerived : Base . But Base.DoProcess takes an InternalData as an argument, so ExternalDerived cannot implement it (because it does not know about InternalData ). Base internal, cost-free constructor prevents the creation of any ExternalDerived instances, and therefore no one will implement ExternalDerived.DoProcess and there is no InternalData , but the compiler does not know this.

How can I rewrite the above code so that the DoProcess(InternalData) abstract method DoProcess(InternalData) and the InternalData class is internal?

+6
source share
5 answers

Set Base as public .

 public abstract class Base {... 

Change Base.DoProcess:

 protected virtual void DoProcess<T>(T internalData) { if (!(internalData is InternalData)) { throw new ArgumentOutOfRangeException("internalData"); } } 

Edit Derived.DoProcess:

 protected override void DoProcess<T>(T internalData) { base.DoProcess(internalData); // Other operations } 
0
source

To make InternalData internal, DoProcess must be private or internal (or InternalAndProtected , but C # does not support this CLR function). It cannot be protected or protected internal .

 internal abstract DoProcess(InternalData internalData); 

Maybe I will also add an internal abstract void DoNotInheritFromThisClassInAnOutsideAssembly() element. This prevents inheritance from anyone outside the assembly because they cannot implement this element and they get a reasonable compiler error. But you cannot make the Base class itself.


I would consider reorganizing the code, so that you do not have a common base class. Perhaps using some internal interfaces and composition.

+4
source

As it smells, you should use composition instead of inheritance . Sorry, this is a very vague answer. Now I think about it in more detail.

+1
source

The base type must be available, because otherwise it becomes impossible to determine its base. Your Base comes directly from System.Object , but how does the Derived user know? How does he know that Base does not come from another public type, and members of this type must be accessible?

If you check everything in Base internally, with the exception of the class itself, you have already forbidden other assemblies from doing anything useful with it. In other words, if you make DoProcess internal, you can prevent InternalData from public .

Yes, admittedly, this makes mistakes in your own assembly if other classes try to call DoProcess . Unfortunately, there is no access modifier "available from derived classes in the same assembly", only "available from derived classes", "available from the same assembly" and "available from derived classes and accessible from the same assembly". (Actually, .NET supports it, but C # does not.)

+1
source

This is actually quite simple. You simply require the derived class to implement the abstract internal method. Classes outside the library will not be able to implement the abstract method and thus will not be executed at compile time.

Your example, minimized only to the point:

 abstract internal class Base { internal protected abstract void DoProcess(); public void Process() { DoProcess(); } } public sealed class Derived : Base { internal protected override void DoProcess() { throw new NotImplementedException(); } } 
-2
source

Source: https://habr.com/ru/post/909065/


All Articles