This lesson will cover the topic of accessibility modifiers as well as private fields and public properties. Access modifiers are prefixed to classes, class-level variables, and methods, in order to determine the “visibility” of these elements throughout your codebase. There are five different access modifiers, however, you will mainly be concerned with just the public and private modifiers:
You can learn more about access modifiers by going to the official MSDN article describing them:
Don’t get confused by the word “struct.” A struct is a special kind of class that looks just like an ordinary class, but with a technical twist. You won’t have to worry about structs very much, in this series of lessons, so wherever you see it referenced, you can take it to generally mean a class.
Step 1: Create a New Project
To illustrate the difference between these different modifiers, let’s go back to where we left off in the previous lesson and save it to a new project called “CS-ASP_042.” You will recall that we made the Character and Dice classes, both from an outside assembly namespace, prefixed as public:
Step 2: Default Protection Levels
If you were to remove these modifiers, the classes would default to the internal protection level. This means that it’s visible by other classes within the same namespace/assembly (as in “internal to this assembly”):
However, since the CS_ASP_042 assembly is an external assembly, the Default class can no longer “see” these outside classes:
Step 3: Understanding Public vs Private Protection Levels
By setting a class – or a class member – to “public” you are allowing it to be visible across all classes and assemblies in your project. It is the most open access modifier possible. By contrast, private is the most closed modifier possible, which means that whatever is prefixed as private is visible only within its containing class/context:
Here, num1 is private and therefore only visible to another member within the same class that wants to reference it:
However, since it is private to the context in which it was declared it won’t be visible outside of this context (in another class, for example):
As you see, in this example, Intellisense doesn’t even show the element that is private to the SomeClass context. That alone is a common reason to keep elements private. Otherwise, if everything was public, a codebase would quickly be overwhelmed with unnecessary options that have no practical use and would only serve the potential for confusion and errors. Furthermore by keeping internal implementation private where possible, you reduce inter-dependencies in a codebase. This de-coupling of code dependencies further reduces complexity and errors. With that in mind, access modifiers are a powerful way of enforcing a key Object-Oriented-Programming tenet called “encapsulation” that encompasses the concept of abstraction and hiding implementation details.
While outside the scope of this lesson, the concept of coupling in Object-Oriented Programming is an important one. The basic idea is that, whenever a class “knows” a lot about another class, and depends on it to perform some of its own functions, the classes become inextricably linked or coupled together. So, when one class has to change – in order to deal with some update, for example – the other class may also change. The problem becomes especially difficult to manage when a variety of other classes also have some sort of dependency on the state of the changed classes. This can lead to a highly unmaintainable codebase. You can imagine how deep of a problem this represents in software development where it’s common for developers to throw out unmaintainable code and simply start from scratch.
Step 4: A List of Access Modifiers
Here is a list of the default access modifiers for the most common code elements (“default” here means whenever the access modifier is omitted):
There is one other modifier that we haven’t gone over yet and that is “protected.” This modifier sits somewhere between public and private. It grants visibility, across all assemblies (unless the “internal” modifier is added), to any class which shares an inheritance hierarchy. Inheritance is a topic that we will be looking at later, however, just keep this in mind for now.
Step 5: Understanding Fields vs Properties
Up to this point we have been talking about class members as, either, variables called “properties” or executable code blocks called “methods”. However, there is another type of common class member, and that is a field. Fields are closer to what you’ve come to think of as variables than properties. Properties and fields both hold values, or object references, that can be read from, or written to. However, properties have their own code block (squiggly brackets) that can handle specific implementation details, somewhat similar to a method. We have only seen the simplest version of this in what is called auto-implemented properties, where the get and set code is simplified, and hidden away. Here, the Sides class-level variable is an auto-implemented property and below it is the random class level variable that is an ordinary field:
Here the field is prefixed as private because we have no intention of letting outside classes access it directly. However, the Roll() method can still access it because, both, the field and the method belong to the same class. The Roll() method, meanwhile, is publicly visible to all
classes. The result of this is that outside access to the random field is indirect; it is hidden behind the implementation details of the Roll() method. This kind of implementation – a private field “hidden” behind a public method that implements it – is very similar to how properties work. An auto-implemented property is just a shorthand for having a private field hidden behind the implementation of a public property. Here is what is actually going on behind the simple Sides get and set:
A private field is created.
A public property is created.
The property can read (get) whatever value is held in the
The property can write (set) to the backing field whatever value is being assigned to it elsewhere in code (“implied by value”).
To get the code-snippet shortcut for a full property (along with its backing field) type "propfull" and then hit the tab key twice.
Here in the Character class we’re attempting to set the Sides property (which actually sets the value to the private sides field) and that works fine because, currently, the property’s set is public:
Step 6: Access Modifiers on Property ‘get’ and ‘set’
However, you can modify the property so that it can be read from (get) publicly, but written to (set) privately:
This renders it impossible to set this value in the Character class. However, since the get is still public you can still retrieve the value publicly:
Step 7: Why Use ‘private set’
Perhaps this private set implementation is used because we are afraid of someone – another programmer using our codebase, for example – giving a value for this property that is not valid, and we want to keep this validation check as a helper method within the Dice class. However, because a property is really just a “gate keeper” for the backing field and has its own code block it can execute whenever a get/set is attempted, and we can put this validation check directly into the property itself. Here, if we try to set the property with anything greater than six – which in return actually sets the private field – a runtime error occurs (you can add specific exception handling):