Variable declaration. Public vs indirect public?

So when I create a public variable that needs to be assigned by another class I do it like so.

public string variableA = "";

But when I look at some other people’s code, I often see this indirect way to declare a public variable.

private string _variableA = "";

public string variableA {
    get{
           return _variableA;
    }
    set{
          _variableA value;
     }
}

Now I often use private variables that I prefix with the underscore if only that class uses them, but why declare public variables this way?

Thank you.

Unity made a design decision (maligned by some) that whether a variable is serialised and displayed in the inspector is influenced by its access modifier. private variables are, by default, not visible (although can be made to be using [SerializeField]). public variables are visible (although can be made not by using [HideInInspector] and/or [NonSerialized]).
Properties are never displayed in the inspector (unless you use a custom inspector component).

Unity itself seemed to change its in-house style used in examples and tutorials from public to [SerializeField] private between 4.x → 5.x, so you’ll see variation in the official examples and documentation. A lot of odd code workarounds that look like they are written to do with access level of certain variables are actually to do with exposing variables to the editor so that they can be changed at edit-time.

Depending on your CS background, you may feel uncomfortable with the idea of making public variables that are not accessed through a property, although in Unity there really is no reason not to. In the code above, the “indirect” property in the second example adds no value whatsoever, unless the template was placed there to allow for additional validation code to be added to the setter at some point in the future.

Usually get/set accessors are used when some kind of processing may need to be performed every-time the variableA is read or written. You can put just about any kind of processing you like, inside those get and set functions. Usually this includes stuff like, setting dirty flags, and doing validation, but can also get pretty advanced and do stuff like buffer-large-data-storage and launch co-routines.

Additionally, with inheritance, you can automatically allow children of your class to apply their own processing of variableA, which may be different from that of the parent, and other children. So, when programmers want a base class to have maximum flexibility, they make all the public “variables” out of accessors.

These are called “properties” and it’s a way to better hide your internal object state from outside influence. For base types like numbers and strings it’s probably not a huge deal to just expose the variable if the value doesn’t matter beyond the data type. But if you need to only accept certain values properties are a good way to validate the value while still treating it like a variable…

For example, when a property is being set you can validate the range:

public float SomeRangeValue
{
    get { return _rangeValue; }
    set
    {
        if (value < minRange || value > maxRange)
        {
              throw new ArgumentOutOfRangeException(...);
        }
        else
        {
             _rangeValue = value;
        }
    }
 }

And you can do calculations in the getter of course, which can be valuable. You could do the same with a function, but accessing a calculated field like a normal variable is sometimes arguably cleaner for the caller.

You can also do read only properties by only specifying a getter…

public int MyValue { get { return _myValue; } }

A read only property can be valuable if you want to expose an array property. Users of the object can access the array elements then, but they can’t replace the array itself.

You can even do write only by just specifying a setter. There’s probably a use case for that but I’ve never seen one. Something security related maybe.

So… properties are technically the “right” way to do things in an object, but there is some performance cost and can often safely be ignored.