Note that a class is a reference type. So, variables of a class type automatically contain references and you can assign the same reference to another variable. You can only create an object (i.e., an instance of a class) if the class is not static. Then you can assign it to a variable.
The fields or properties must not be static. Non static members are called instance members. Each instance (object) has its own copy of the fields and properties that must be accessed through a variable name. Static members, in contrast, are shared among all objects of this type and must be accesses through the class name.
Stats stats1 = new Stats();
Stats stats2 = stats1;
now both variables reference the same Stats. If you make the change
stats1.health = 5;
then stats2.health
is also 5 since both reference the same object. But of course, you can create independent Stats
objects:
Stats stats1 = new Stats();
Stats stats2 = new Stats();
Now changes to stats1
do not affect stats2
.
Note that an important idea of Object-Oriented Programming (OOP) is that objects should hide their internal state, i.e., their fields. From outside the state should only be accessible through methods. These methods ensure that the state is manipulated in an adequate manner. E.g., it could be ensured that the health stays within a valid range.
Properties are specialized methods allowing to manipulate the state of fields. They usually consist of a pair of get
and set
methods and can be accessed like a field.
Example:
class Stats
{
private int _health = 10;
public int Health
{
get { // called when reading the value: int h = stats1.Health;
return _health;
}
set { // called when setting the value: stats1.Health = 5;
if (value < 0) {
_health = 0;
} else if (value > 100) {
_health = 100;
} else {
_health = value;
}
}
}
}
If a property has no such logic, you can use an auto implemented property. It automatically creates an invisible backing field (like _health
) and returns and sets its value.
public int Health { get; set; }
Let us put the things together. Simple example of Stats
class:
class Stats
{
public int Health { get; set; } = 10;
public int Strength { get; set; } = 5;
public int Defense { get; set; } = 20;
}
Now you can reference a Stats
object in the Potion
class.
Because you want to have different kinds of potions, you can use inheritance (another important concept of OOP) to achieve this.
You can declare an abstract base class, i.e., a class that cannot be instantiated and can itself contain abstract members, i.e., members that have still to be defined in derived classes.
abstract class Potion
{
// This is the "reference holder" variable you were asking about.
protected Stats _stats;
// Protected means private and visible to derived classes.
protected int _magnitude;
public Potion(Stats stats, int magnitude)
{
_stats = stats; // Save the reference to the stat to the "reference holder"
_magnitude = magnitude;
}
public abstract void UsePotion();
}
Now the derived LifePotion
class as an example
class LifePotion : Potion // Inherits Potion.
{
public LifePotion(Stats stats, int magnitude)
: base(stats, magnitude) // Calls the base constructor.
{
}
public override void UsePotion()
{
_stats.Health += _magnitude; // Change a property of the referenced variable.
}
}
Repeat the same for StrenghtPotion
and DefensePotion
classes with UsePotion
setting the Strength
and Defense
properties.
The adapted main program
class Program{
static class Main(string[] args)
{
var stats = new Stats();
Potion lifePotion = new LifePotion(stats, 5);
Potion strenghtPotion = new StrengthPotion(stats, 5);
Potion defensePotion = new DefensePotion(stats, 10);
lifePotion.UsePotion();
strenghtPotion.UsePotion();
defensePotion.UsePotion();
Console.WriteLine(stats.Health);
Console.WriteLine(stats.Strength);
Console.WriteLine(stats.Defense);
}
}
Note that you can override ToString
in a class and provide your own implementation. Add this to the Stats
class:
public override string ToString()
{
return $"Health = {Health}, Strength = {Strength}, Defense = {Defense}";
}
Then you can print the health like this in the main routine:
Console.WriteLine(stats); // Prints: Health = 15, Strength = 10, Defense = 30