This chapter finally explains why we have to add public in front of all of our struct variables. If you remember, without the public, you can’t use them, which seems like a crazy rule.
The actual rule is that everything is either public or private, and if you don’t
say, they’re automatically private. But that doesn’t explain why we have private
in the first place.
This chapter is about how private variables work, why we’d ever want to use
them, and a few similar tricks.
private really means that people outside of the class aren’t allowed to use it. Member functions in the class are allowed to see all the fields. You can think of private as employees-only.
Here’s an example class with two private variables:
This is not a good class, but it shows how it’s possible to indirectly use private variables through member functions:
We can never touch n and m, but we can change and read them through functions.
Later we’ll have an example where that’s useful.
We can also have private member functions. We can’t call them, but they aren’t useless since other functions can. Here fixNeg is private, but Set uses it to fix the inputs:
The pop-up hides private’s from us, which makes them less annoying. Typing nh-dot will only show Set, getN and getM.
For our classes so far, we started out knowing the variables. For example, FullName
started with us wanting one string for first name and another for last name. Turning
it into a class was just a nice way to group them. The member functions are merely
helpful shortcuts. There’s nothing wrong with that, but private wasn’t made for
There’s a different way to use classes. Sometimes we start with something we want to make. The variables are just a way to make it happen, and we really don’t care about them. We only care about using the member functions.
private was invented for this idea – we can use it to say “don’t think about the
variables – the functions do what you need.”
Here’s an example. Suppose we want a random number roller that can remember the range, and never rolls the same number twice in a row. The interface (the public functions) can be like this:
Just those functions are all we need for a useful class. We can say:
RollerNoRpt r1; r1.SetRange(5,10);, and then use
int n=r1.Next(); to get 5-10’s with no repeats.
Now that we like those functions as the interface, we have to write them, along with variables to make them work. We’ll save the range, pre-adding 1 to the max, to account for how Random works. We’ll have a variable saving the previous number:
So far, by hiding the variables we’ve made it impossible to get them backwards: If
you call SetRange(7,2) by mistake, it fixes it.
Next() will use the “roll until it’s not a repeat” loop from before, and the private helper function simpleRoll:
That proves that we can do it, and it’s some fun coding. But the important thing
is how it’s completely hidden. Users type r1-dot, see SetRange and Next, and that’s
all they need.
Another example, a little simpler: we want a class to check whether an x,y is inside a rectangular area (maybe for a 2D game).
We’ll give options to set an area starting from either the lower-left corner, or from the center. For example r1.SetFromCent(6,6,8,4) puts the rectangle at (6,6), 8 wide and 4 high. Or r1.SetFromLL(0,0,5,5) puts the lower-left corner at (0,0), 5 wide and high. Some situations will prefer one or the other.
No matter how we set it, r1.isIn(p1) checks whether p1 is inside. We don’t care
how it checks, or what variables it uses, as long as it works.
The plan is to store the real positions of all four corners. If we use SetFromCent with x=50 and 8 wide, we’ll save 46 and 54 for x min and max:
That’s all fun code. I especially like how isIn is merely two in-between compares
this way. But the main thing is how it’s hidden. Users only care about SetFromCent,
SetFromLL, and isIn. They can’t even see the variables.
This next example takes that idea a little further, making a 0-255 color class which purposely hides how Unity Colors use 0-1 (paint programs all use 0-255 for color levels. It’s the range all artists know).
Here’s the outline (it’s a little fakey, but it’s short and shows the idea):
We can make bright orange with c1.set(255,128,32);. Then use
c1.applyTo(cat.transform); to paint the cat orange. We never see a 0-1 value.
To store them, we’ll use a real Unity Color variable, and a private helper conversion function:
toF is a typical private helper function. It’s used by Set to convert each 0-255 into the real 0-1. We don’t want the user to see it, since the entire point of this class is hiding how color is 0-1.
The class only has one variable, which is fine for classes like this.
There are some concepts and terms that go with “use a class to make an idea.” They
aren’t really technical terms, but people use them a lot, so it’s nice to hear them, and
some can be helpful.
We think of a class as divided into Interface and Implementation. Interface is the normal English meaning – the part you interact with. For a class, it’s the public functions. Implementation is how it actually works.
Anyone using the class is a client. Clients use the Interface, and don’t care about
This is really the same trick we’ve been using with functions – we only need to
know the inputs and output, not exactly how it works. For a class, the inputs and
outputs are what you can do with all the public functions.
Another word for the idea is Information Hiding. A slightly newer term
is Encapsulation – the private implementation is encapsulated away from
Sometimes we call a class like this an Object. That’s where the term Object Oriented Programming comes from.
Object is another way of saying we’re absolutely not thinking of it as pile of
Here’s a list of some regular times people like to use private to break into Interface vs. Implementation:
Sometimes it’s the case that we know exactly what’s inside of a class, but we still want to force ourselves to use the interface. Suppose we have a score that should also be displayed in a textBox. We could make a simple class to group them:
It’s the user’s job to remember to change the label when they change s. But at the very least we can make a helpful function for that:
Users can add to the score with: s1.s++;, then run s1.updateLabel(); to display it. But someone, somwwhere, will forget to run updateLabel(). The score will change, but we won’t see it. To avoid that we need one command to do both things at once, which you have to use:
To be nice, it’s two commands. s1.Set(5); seems fine, at first, but s1.ChangeBy(1); is an easy way to add 1. The key is that both update the label. You can’t get them out-of-synch.
Making s private caused a problem: we can’t read the score any more. That’s
what s1.score() is for. We’ll have to write ugly things like if(s1.score()>21).
But always having the score and label change together will be worth it.
The best way to assign the label is a bonus trick. We need to give it a textBox once, at the start, and won’t change it again. A neat way to allow that is by putting it in the only constructor:
It’s a neat trick since we need to use new anyway, and it’s impossible to forget to supply the label:
This is one typical use of private. It’s a way to say “I know that you know how to use the variables, and it would probably be fine. But, to be safe, please, please always call the functions instead”.
A fun way to see the Interface/Implementation idea is to rewrite how a class works
inside, without changing what it does.
The Rectangle class could be rewritten to secretly store the center and width/height (the old version stored the positions of the corners). This isn’t super-exciting:
I think the old way is better. But the point is that both ways – completely
different variables – look and act exactly the same to users. That’s pretty
The random roller can have a much better rewrite. We probably want it to hit every number before repeating: for 1-6 it could be 4,3,6,1,5,2, then go again.
We can use the shuffle trick to do that. It starts with a hidden list of the scrambled numbers, walks through them as we ask with Next(), and rescrambles when we run out:
The pop-up still shows only SetRange and Next(). Users know nothing about a list and shuffling, which is great. Those are details they don’t need to think about.
Hiding a variable to make you use functions is so common that we have a name for
the functions: we call them accessors or a “getter/setter” pair.
The Score class is a typical example. Everyone knows the class has an integer score variable. We’re not trying to keep you from thinking about it. We merely want you use use s1.Set(4) to change it. A not-so-good side-effect is having to use s1.score() to read it. Set(n) and score() are its accessors – one to “get” it, the other to Set it.
It’s not great, but we can indirectly use the two functions to do anything = could do before:
We almost always use them to add extra rules about changing a variable – such as also updating the label. A common example is not allowing a variable to be negative. We put a check for that in the Set function:
It’s a little awkward. n1.setN(5) replaces n1.n=5. Not too bad. But
n1.n--; turns into n1.SetN(n1.getN()-1);. Ick, but it ensures n can’t be
There’s one more trick getter/setter pairs allow. They can create fake variables. For example, this class stores the distance in feet, but can pretend it uses inches:
If you prefer to use this class with inches, you can. Use n1.setInches(18) and
n1.inches() gives you back 18. For real it sets feet to 1.5, but so what? And if
some other wise-guy sets n1.feet=5 its fine – n1.inches() reads back 60. It works
We can do something similar with Rectangle. We already have a function that sets it using the lower-left corner. We can rename it and add one to compute the lower-left corner:
Combined, they allow us to examine and position Rectangles as if lower-left was the actual variable.
The getter/setter idea is so popular that C# has a super-special shortcut. The idea is this: getters and setters are substitutes for x=n1.n; (getter) and n1.n=3; (setter). What if we change =’s so they automatically run the appropriate get or set?
You still have to write both functions, in the class. But you don’t have to use that messy syntax to call them. Even something tricky like n1.n++; will automatically run your getter for n, add 1, then run the setter.
In this legal C# code, get, set and value are magic words, specially for this trick. n can’t be negative:
The funny syntax (it looks like a function named n, except with no parens for the input) is the special rule. get and set aren’t real function names. int x=n1.n; magically calls get and n1.n=3 magically calls set with value at 3.
The details aren’t all that important. This trick is a style thing – if everyone else
uses it, so should you. Hopefully it helps show the getter/setter concept.
It’s also a fun example of language design. Older languages thought of this trick and rejected it. n1.n=4; being a secrete function call seemed too confusing. C# decided, why not?
Many different languages are that way – the features they do or don’t have are opinions of the designers.