In a previous post we talked about the new in keyword in C# 7.2, which got me going through the new features in C# and how they all fit together. One of these new features is the private protected access modifier. Now you’ve heard of marking properties/variables with private , and you’ve heard of them being marked protected , but have you heard of them being marked private protected ?! Next level! Let’s take a look.
How Protected Currently Works
First, let’s look at a basic example of how the protected keyword can be used. A member that is marked with only protected, can only be accessed within derived classes, but the derived class can be in any assembly. So for example, in “assembly1” we have the following code :
public class BaseClass { protected string BaseMember { get; set; } } public class DerivedClass : BaseClass { void DoSomething() { var member = BaseMember; } } public class ServiceClass { void DoSomething() { var baseClass = new BaseClass(); var member = baseClass.BaseMember; // Error. } }
In this example, our DerivedClass has no issues accessing the BaseMember because it has inherited the base class. However our ServiceClass throws a compile time error that it cannot access the member from an instantiated class. Makes sense.
In a second assembly, for arguments sake let’s call it “assembly2”, we create the create the following class :
class DerivedClassInAnotherAssembly : BaseClass { void DoSomething() { BaseMember = "something"; } } class ServiceClassInAnotherAssembly { void DoSomething() { var baseClass = new BaseClass(); var member = baseClass.BaseMember; // Error. } }
This essentially has the same effect as what we did in assembly1. Our derived class still works even though it’s in another assembly, because protected doesn’t care about where the derived class is actually located, only that it’s inheriting from the base class. And our service is still broken as we would expected.
So that’s protected done.
How Internal Protected Works
So a little known access modifier that has been in the C# language for a while is internal protected . Now at first glance you might think that it’s an “and” type deal. Where the member is internal and protected e.g. It can only be accessed within the same assembly. But actually it’s an “or” situation. You can access the member if you are inside a derived class or you are within the same assembly.
Going back to our example. Inside assembly1 if we go ahead and change the BaseMember to be internal protected :
public class BaseClass { internal protected string BaseMember { get; set; } } public class DerivedClass : BaseClass { void DoSomething() { var member = BaseMember; } } public class ServiceClass { void DoSomething() { var baseClass = new BaseClass(); var member = baseClass.BaseMember; // Now has no issue. } }
We can now see that inside our ServiceClass, when we instantiate the instance of the BaseClass, we can now access the BaseMember. And our DerivedClass is still trucking along as per normal. This is where the “or” comes in.
If we head to our second assembly (assembly2). And recheck our code there :
class DerivedClassInAnotherAssembly : BaseClass { void DoSomething() { BaseMember = "something"; } } class ServiceClassInAnotherAssembly { void DoSomething() { var baseClass = new BaseClass(); var member = baseClass.BaseMember; // Error. } }
Nothing has changed. Our DerivedClass can still access the BaseMember, but our ServiceClass that is instantiating up an instance cannot.
How Private Protected Works
Now we are onto the new stuff! Before we get into explanations, let’s keep running with our example and change our BaseClass to use private protected and see how we go.
First assembly1 :
public class BaseClass { private protected string BaseMember { get; set; } } public class DerivedClass : BaseClass { void DoSomething() { var member = BaseMember; } } public class ServiceClass { void DoSomething() { var baseClass = new BaseClass(); var member = baseClass.BaseMember; // Error. } }
So we have changed our BaseMember to being private protected. Our DerivedClass still doesn’t have an issue, but our ServiceClass is throwing up an error at compile time again. So far, it’s working just like how protected has always worked.
Onto assembly2 :
class DerivedClassInAnotherAssembly : BaseClass { void DoSomething() { BaseMember = "something"; // Error } } class ServiceClassInAnotherAssembly { void DoSomething() { var baseClass = new BaseClass(); var member = baseClass.BaseMember; // Error. } }
What have we here!? So now in our second assembly, even our DerivedClass is now throwing up an error even though it is inheriting the BaseClass. Our ServiceClass is still in strife, so nothing has changed there.
From this we can see that using private protected means basically what I originally thought internal protected meant. It’s protected so that only derived classes can use it, but it’s also only for use within the same assembly.
If you haven’t already gathered, I’m not a big fan of the naming. The internal keyword already sparks off a “only within this assembly” lightbulb in a developers head, so to then use the private keyword to limit a member across assemblies seems like an odd choice. But internal protected was already taken, so it’s a bit of a rock and a hard place.
What do you think?
In Chart Form
In case you are still a little confused (And because things are always better in chart form), here’s a handy chart to make things just that little bit easier.
Derived Class/Same Assembly | Instance/Same Assembly | Derived Class/Different Assembly | Instance/Different Assembly | |
---|---|---|---|---|
protected | Yes | No | Yes | No |
internal protected | Yes | Yes | Yes | No |
private protected | Yes | No | No | No |