Access modifiers in C# 7.2
January 22, 2018 Leave a comment
C# 7.2 comes with a new access modifier “private protected”. It sounds like a weird combination so let’s see how the various access modifiers in the current release of C# work.
Let’s start with an abstract class called Animal:
public abstract class Animal { private String privateName = "Private name"; protected String protectedName = "Protected name"; protected internal String protectedInternalName = "Protected internal name"; private protected String privateProtectedName = "Private protected name"; public String publicName = "Public name"; }
…with another object that derives from Animal:
public class DerivedAnimalInSameAssembly : Animal { public DerivedAnimalInSameAssembly() { Console.WriteLine(base.privateProtectedName); Console.WriteLine(base.protectedInternalName); Console.WriteLine(base.protectedName); Console.WriteLine(base.publicName); } }
Note the name of the class with “InSameAssembly” attached. So here we have a public abstract class and another class that derives from it. Both are assumed to be in the same assembly, e.g. a C# class library, which we can call MyCompany.API. DerivedAnimalInSameAssembly will have access to the following properties in Animal:
- public: this shouldn’t be a surprise to anyone as public members of a class are available from all other places of the same assembly and all other assemblies referencing this class
- protected: protected members are available in the containing class and the derived classes
- protected internal: this is available in all derived classes in any assembly and any class in the same assembly that builds this object
- private protected: we’ll get back to this in a bit
DerivedAnimalInSameAssembly won’t be able to reference the private field of Animal and the reason should be clear to all .NET developers: private members are only available in the containing class.
Now let’s see what happens if we instantiate a DerivedAnimalInSameAssembly in the same assembly:
public class PrivateProtectedAccessModifierDemo { public void RunDemo() { DerivedAnimalInSameAssembly animal = new DerivedAnimalInSameAssembly(); Console.WriteLine(animal.protectedInternalName); Console.WriteLine(animal.publicName); } }
“animal” will be able to access the public and protected internal members of DerivedAnimalInSameAssembly. The PrivateProtectedAccessModifierDemo doesn’t derive from Animal or DerivedAnimalInSameAssembly but is in the same assembly as Animal hence it has access to all protected internal members of Animal. However, PrivateProtectedAccessModifierDemo cannot access the protected members of Animal/DerivedAnimalInSameAssembly for the same reason: it doesn’t derive from either of them.
Let’s now see what happens if we build a class in an assembly different from Animal, e.g. in another C# class library:
public class DerivedAnimalInAnotherAssembly : Animal { public DerivedAnimalInAnotherAssembly() { Console.WriteLine(base.protectedInternalName); Console.WriteLine(base.protectedName); Console.WriteLine(base.publicName); } }
DerivedAnimalInAnotherAssembly has access to the same members of Animal as DerivedAnimalInSameAssembly with the exception of the “protected private” member. Let’s see the various stages of accessibility:
- public: any other class, any assembly
- private: only the containing class
- protected: the containing class and derived classes in any assembly but not in classes that do NOT derive from this class
- protected internal: the containing class, derived classes in any assembly and any class in the same assembly that instantiates this class
- private protected: the containing class, derived classes in the same assembly but NOT in classes of other assemblies even if they derive from that class
Hence “private protected” adds a small restriction compared to “protected” and “protected internal”.
View all various C# language feature related posts here.