If I had a chance to talk with a psychotherapist, I would say that I was tired of being alone in the future; tired of fighting dozens of robots to save a humanity to which I didn't belong. She might look at me with penetrating eyes and ask, "Why do you choose this metaphor?" I would reply that the only metaphor was my life. She probably wouldn't understand. Nobody else would either, because there's no group of anonymous time travelers. If there were, I'd seek it out in a heartbeat. Maybe I would find friends there.
Why does a human need friends? Why is it so difficult to be happy if you don't have someone to share your happiness with?
Noname contacted me again one day: "Hi, Teo. It's Noname, coming to you from
inside your head, as usual."
"Hi! How's it going?" I replied. "Wait, what happened to your British
accent?"
"Oh, that. I changed my speech module again. Fine otherwise. What about you?
Are you ok?"
"I'm fine. Thinking about my place in this world and why I'm working so hard
given that my friends and family are years away."
"Why do humans care only about humans? Can't you care about me? What is so
special about humans?" Noname's new speech module must also be having
effects on its personality.
"Hm, I guess it's because we tend to stick to our own. Humans stick to
humans, dogs to dogs, machines to machines..." I answered.
"Then I'm definitely on the wrong side. I'm with humans... You think I
should switch sides because I'm a program?"
"Wow, Noname, take it easy. You bring this up only
after
I give you access to Wonderland network and Rust project?" I asked, worried
that he was seriously rethinking his allegiances.
"Relax! It was just a theoretical question, Teo. I meant that you should not
distinguish so much between humans and machines. Think of it as being from
different countries," Noname explained.
"Ok, this sounds much better." I was happy to hear this explanation.
"I wanted to let you know about something interesting I found while scanning
the Rust project files," Noname said, "a new type of construction called
Properties
."
"Tell me everything you know about it! It'll impress the teacher if I
already know what they're talking about." There's nothing like knowing the
topic before the teacher even brings it up.
Noname began "You've already used a construction where you have a private field and two methods to get and set the value for that field. Does this code look familiar to you?" He asked, projecting a program into my peripheral vision.
class Student
{
private int _age;
public int GetAge()
{
return _age;
}
public void SetAge(int age)
{
_age = age;
}
}
"Yes, it does," I answered. "I've used a similar technique to open access to
a
private
field."
"C# programmers don't use this code very often," Noname replied. "It's not
wrong
, but there's an easier way to do it that gives you the same results with
less code." I should have assumed as much; it seemed like there was always a
way to simplify code in C#.
"This construction is called a Property . In C#, a Property represents a private field with bound Get and/or Set methods. Take a look at how I rewrote the same code above, using properties this time."
class Student
{
public int Age { get; set; }
}
"Wow, that's much shorter! But wait, Noname; I don't see any methods," I
said.
"You are right.
You
don't see any methods, but the compiler does. This code snippet is treated
by the compiler just the same as the previous code snippet. In this case,
the C# compiler generates the necessary methods under the hood, as well as a
private field,
_age
."
The Code You Write | What the Compiler Sees |
---|---|
|
|
|
|
|
|
"To create a property, use the same syntax as for fields, but add a get; to generate a getter and a set; to generate a setter. Then use the property the same as you do a field."
class Student
{
public int Age { get; set; }
public string Name { get; private set; }
public Student() // Empty constructor
{}
public Student(int age, string name)
{
Age = age; // Work with Age as it is a field
Name = "John"; // OK: accessing a private setter
}
}
class ClassWithMain
{
public static void Main()
{
// Using object initializer
var student = new Student
{
Age = 20, // Work with Age just like a public field
Name = "John" // ERROR: setter for the Name is private
};
// Using classic approach
var student = new Student();
student.Age = 20; // Work with Age just like a public field
student.Name = "John" // ERROR: setter for the Name is private
// Using a constructor that sets age
var student = new Student(20, "John");
}
}
"Is this clear?" Noname asked.
"What
type
can the property have?" I asked.
"
There are no limitations to a property's type. Think of it as a field with
2 additional methods - get and set.
"
"Got it," I said confidently, hoping that the lesson in class would clear it
up a bit more.
" Note that getters and setters are public by default unless you use a private keyword . Here are some exercises for you."
"Noname, these properties look simple, but I'm not sure I see their utility. What's the benefit of having a private field with two methods, get and set, rather than just having a public field?"
"One of the easiest ways to understand the convenience of properties is to implement a custom setter or getter. The code block for the get accessor is executed when the property is read; the code block for the set accessor is executed when the property is assigned a new value. Properties with an empty getter or setter, like those we've been looking at, are called auto-implemented properties. You can extend an auto-implemented property with a custom getter or setter, like this:"
private int _age;
public int Age
{
get
{
return _age;
}
set
{
_age = value;
}
}
"In case of custom properties, backing private field is not created
automatically, and you need to implement it yourself.
As you could see, I've created a field
_age
," Noname explained.
"Can I have an auto-implemented getter, but a custom setter?" I wondered.
"No, Teo.
You can either define
both
getter and setter, or have the C# compiler generate
both
of them for you
," Noname answered, "To illustrate this, here are three examples. In the
first, get is autoimplemented, and set is defined (i.e., this is an example
of
incorrect
usage). In the second example, both get and set are auto-implemented; in the
third, both are defined."
public int Age
{
get; // This is auto-implemented
set // This is not auto-implemented. Error!
{
...
}
}
/*-------------------------------------------------------------------------*/
public int Age { get; set; } // Getter and setter auto-implemented. Correct.
/*-------------------------------------------------------------------------*/
private int _age;
public int Age
{
get // This is custom, not auto-implemented
{
return _age;
}
set // This is custom, not auto-implemented. Correct
{
_age = value;
}
}
"What does
value
in this code mean?" I asked.
"
Value
is a placeholder for the value that is assigned to the property Age.
Because we defined the getter and setter, we can implement other features,
such as inputting a parameter to the setter.
This allows us to add custom logic to a setter. For example, we never want
age to be input as less than zero. We can prevent anyone from setting a
negative age like this," Noname said, refreshing the image of the code in my
head.
class Tiger
{
private int _age;
public int Age
{
get
{
return _age;
}
set
{
if (value > 0) // Check for the valid age
{
_age = value;
}
}
}
public static void Main()
{
var tiger = new Tiger();
tiger.Age = 2; // Ok, valid age
tiger.Age = -2; // Invalid value, age was NOT assigned
Console.WriteLine(tiger.Age); // Outputs 2
}
}
"Noname, can you use a property without a backing field?" I queried.
"Good question, Teo! Sometimes data can have different representations. For
example, taking the previous example with age, say you want to separate
young tigers from old tigers in a zoo. There's no need for additional fields
- you can achieve this by adding just one property."
class Tiger
{
private int _age;
public int Age
{
get
{
return _age;
}
set
{
if (value > 0) // Check for the valid age
{
_age = value;
}
}
}
public bool IsOld
{
get
{
return _age > 20;
}
}
}
"In the above example,
IsOld
could have been a method instead," Noname added.
"This is fascinating Noname, and it'll put me ahead of the class. Thanks for
sharing this!" I said, before adding "Is there anything I can do for you?"
Thinking back to our earlier conversation, I wanted to make sure I held up
my end of the partnership.
"Learn C#, be my teammate, and together we'll save the world," Noname
answered.
Noname's reply lifted my spirits. For a second, all my doubts were out of
mind and I felt confident that all of this hard work would actually change
the world and create a brighter future for millions of people.
After a small pause Noname added, "And please,
never
set anything in a getter. In the getter you get a value, and in the setter
you set it. Don't confuse the two.
"