Search Unity

SerializeField vs public (and vice versa)

Discussion in 'Scripting' started by APSchmidt, Jan 12, 2020.

  1. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
    I'm not sure "versus" is accurate in this context; "or" would probably be more what it is all about.

    I've recently noticed that some people seemed shocked that using SerializeField was not taught to learners instead of using "public"; they sounded like you should always use SerializeField. I don't feel like it's true: too systematic for my taste.

    So, when is it preferable to use SerializeField instead of public?

    And what about the other accessibility modifiers? When is it preferable to use them?

    Serialisation C#

    Serialisation in Unity
     
  2. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    916
    Huh. This is quite likely going to turn into a kind of religious debate once the purists show up (they probably are those same people who are shocked by the incorrect use of 'public').

    IMHO (and I can't stress the 'MH' part of that enough), there are two ways to go about this: the practical, and the principled. To practical people, scripts are a tool to get things done, and they want to be able to use Unity Editor to put some data into their components. SerializeField is one way of doing it, declaring a variable public is another. To many (if not most) the difference between these is moot, as long as they attain their goal. As long as it works, don't bother if it's the 'politically correct' way.

    The principled people will point to the difference between a public and a private variable, and tell you that you should adhere to the design principles or you might run into problems somewhere down the road. This is true (especially that you 'might' part; it's only a possibility); but to most developers in the context of Unity coding that is not relevant.

    Because this is (to me) the hard truth: there are very few Unity projects around where a clear distinction between private and public attributes/variables is even relevant (in self-written scripts). Most Unity projects are small affairs, where the code grows organically instead of being architected in advance. Most people who write Unity scripts never subclass their own scripts, so it really doesn't matter if their attributes are private, protected or whatever. The most important question is 'can I see and modify it in Edior?'- 'Plublic' does that. Since you don't subclass anyway, it's not a problem. Since your projects are small (i.e. a team of fewer than 5 coders), class architecture isn't important. [SerializeField] has AFAIK no performance connotations, so that's not an issue either. From a practical standpoint a 'public' variable is simply a variable that is accessible from Editor, and for some more seasoned coders it's also something that they can access from another script.

    So, from the perspective of someone tasked to teach how to write *Unity scripts*, using 'public' to make a variable appear in Editor (aside from being able to access from other scripts) is a good choice, as it avoids muddying the waters with SerializeField. People who learn to code in Unity want to learn how to use a tool effectively, not the theory behind OOP - for that they'd (hopefully) look to some other tutorials.

    I've been on the other side of that debate (pragmatism vs purism) when it comes to using setters/getters, so yes, I do realize that there is no absolute right or wrong here. But for most intents and purposes I feel that it is OK to forego the formal differentiation between public, private and protected variables, and sell the difference as a convenient way to access variables in Editor -- when you teach *Unity* programming. I'll probably feel otherwise when you are running a formal programming course, where such things are the bread and butter and constitute the underpinings of formal code design.

    Well, here comes the hurricane...
     
    Last edited: Jan 12, 2020
    APSchmidt likes this.
  3. mbaske

    mbaske

    Joined:
    Dec 31, 2017
    Posts:
    136
    And here I thought SerializeField exists so you don't have to forego good coding practices in Unity. Do other classes need read&write access to a variable? If no, declare it private/protected. If yes, use getters/setters.
     
    Vharz likes this.
  4. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
    I hope I didn't start a war! (although it might be interesting to read what people have to say about that).

    Thank you for your contribution! I work alone on my project but not being very experienced in coding, I use model scripts written by others so that I can learn how they did what I want to do. In some of these scripts I've seen both public and [Serialized Field] used. My conclusion was that using [Serialized Field] answers specific needs (like preventing public fields to be accessible from other scripts) and there is no reason to use it on a large scale. But then, I stumbled on these people implicitly claiming that public should not be used, which is why I asked here.

    I'm on the side of the "practical people" by the way. ;)

    What do you mean by "subclassing scripts"; I don't think I had heard of that before?
     
  5. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    916
    C# has the very powerful ability that classes ('scripts') can inherit abilities from other scripts. You have seen this before: most if not all your scripts begin with

    Code (CSharp):
    1. class myClass : MonoBehaviour
    This means that your class 'myClass' inherits all existing attributes and abilities from the MonoBehaviour class (which itself inherits from its own parent classes). You ourself can use this ability to create new classes based on your own new 'myClass' if you define them as child of myclss:

    Code (CSharp):
    1. class anotherOfMine : myClass
    2.  
    etc. All variables that you have defined in myClasss will also exist in anotherOfMine. And here it becomes imortant what kind of Attribute you declared. public, private,protected. Since you never bothered before, all that stuff only bcomes somewhat more important when you start sublcassing. In order to use that (quite powerful) technique, you may find an object-oriented programming (OOP) tutorial very helpful and incredibly interesting.
     
  6. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    916
    It does. The OP's question was why it wasn't taught, and my opinion is because 'public' meets the requirement and eschews the OOP baggage for those who just want to code some stuff.
     
    Last edited: Jan 12, 2020
  7. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
    Ah, all right, I knew about that, I just didn't connect to "subclass". :)

    Uh, no, my question was about what to think about people who say that "Serialize Field" should be taught instead of "public" and what to do about it. :)
     
  8. mbaske

    mbaske

    Joined:
    Dec 31, 2017
    Posts:
    136
    I see your point, although I wouldn't consider it a pragmatism vs purism issue. I don't expect people to design a UML diagram before writing their first line of code, but being aware of a few guidelines can't hurt. Public variables in particular are something I've tripped over when I started programming. For an inexperienced coder, they can seem like the obvious choice for changing the state of an object. Which might be ok as long as that object doesn't do much more than encapsulating some data. But relying on public variables instead of methods for control flow is more prone to creating a web of cross-dependencies, making debugging more difficult.

    I think they're right. A variable's accessibility and its visibility in the editor are distinct concepts. Using public only to make a variable visible adds unnecessary confusion imo.
     
    Vharz likes this.
  9. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    916
    I just now realized that your post wasn't just idle speculation, but that you have skin in this game in the form of tutorials you authored. Therefore the question perhaps merits some more thought. So, allow me to re-phrase your question so it better fits my narrative :) -- "How important is adhering to best practice and coding standards when writing tutorials?".

    IMHO, the most important questions are "who is your target audience?", which defines their expected level of expertise, and "what is it you wish to teach?" - obviously, a tutorial on how to tweak a BSP search using a massively parallel processor architecture will assume a lot knowledge on your part, and perhaps use some unconventional exploits that are not within the realm of best practice.
    Example: an eon ago when I was speed-optimizing assembly code to draw pixels on-screen, my senior programmer taught me to use add twice instead of shifting left because the add operation was eight times faster than a shift. Obviously, using add instead of shift makes the program more difficult to read, and is far removed from best practice.

    So, who is your audience, and what do you want to teach? I think that if your aim is to teach programming, best coding practice is paramount. If, on the other hand, your goal is to teach Unity, you should be able to get away with anything that eases getting your points across (i.e. the particulars of Unity). Usually, these are things like integrationg animator controllers, magic methods, getting access to components, co-routines or callbacks/delegates -- the nuts and bolts of Unity, not the underlying scripting language. So any discussion of language-specific keywords such as private, protected, readonly, public etc should be out of scope in a Unity-Tutorial, with the possible exception of explaining the design and workings of singletons in conjunction with scene loading.

    So how about SerializeField? That's a Unity particular as well. personally, I cringe every time I see it being used in a Beginner's Tutorial. That's because obviously, it's a cludge. Dead giveaway: it's name. It describes what it does, not what it is for. It's a deeply technical work-around for a particular Unity-specific feature of Editor. Serialization itself is not a Beginner's topic, and why get into that without real need? You'd be wasting your and your student's energy on off-topic minutiae. It's much easier to note that 'for now, simply declare ary variable you want to access in editor as public'. Yes, it may not be best coding practice. But it will help focus on the important stuff. I feel even more strongly about the use of setters and getters in Tutorials. Personally, I feel you should only use them if there is no way around them (hint: there always is). They are the bane of every tutorial because they implicitly cause execution of code, something that should never happen in a tutorial (as opposed to 'real' code, where it's fair game). And yet I've seen so many Tutorials where the authors unthinkingly distract from the object of their lessons with these unneccessarily difficult (for beginners) constructs.

    tl;dr: I'd focus on the lesson at hand. SerializeField will distract from that lesson if the student doesn't yet know it, and that knowledge itself is non-essential. It's fine to use when covering more advanced topics, when you can assume familiarity with Serialization or Editor particulars. When teaching a Unity Beginner, it should be off-topic and avoided even if it breaks coding best practices.

    If you try to write a tutorial that teaches coding C# using Unity, you are on your own :)
     
    Last edited: Jan 13, 2020
  10. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
    What about protected? Any difference with [Serialize Field]?
     
    Last edited: Jan 16, 2020 at 1:40 PM
  11. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,429
    When you're working in teams, access modifiers are usefull for communication. If I make a method public, that tells other programmers on my team that I expect that method to be used, so they should be able to use it and expect it to say what it does. When I make methods private, that means that I don't expect them to use it, and it might be that the method depends on the internal state of the object to do the correct thing.

    Similarly, a public field means that the code assumes that the field can be changed externally at runtime, and will handle that. a private field means that I expect that it won't be changed at all.


    So my policy is to chose the access modifier based on if the field should be changeable externally or not. If the field should be serialized or not is a completely orthagonal concern.


    "public protected" isn't a modifier in C#. What are you asking about?
     
  12. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
    Protected, of course.
     
  13. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,429
    Well, the same rules goes for me there. Use it when you need the code access at runtime to be protected (ie. only accessible by child classes).

    Make the decision on wheter the field should be serialized or not a completely seperate decision from that.
     
    Last edited: Jan 16, 2020 at 2:02 PM
  14. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
    Okay, what difference is there between the two of them? Is a serialised field not accessible from child classes?
     
  15. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,429
    A private field is not accessible from child classes. All other fields are.

    Marking a field with [SerializeField] doesn't have anything to do with what other classes can access the field. I reformated my post to be a bit more clear that I mean that the two don't relate to each other.


    This would have been a ton simpler if Unity had made it so you always had to decorate a field with [SerializeField] if you wanted it to be serialized, even if they were public. public/private and serialized/nonserialized are different concerns and shouldn't have anything to do with each other, but unity designed them so they are connected by default.
     
    APSchmidt likes this.
  16. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
  17. WallaceT_MFM

    WallaceT_MFM

    Joined:
    Sep 25, 2017
    Posts:
    321
    In practice, I use private serialized fields almost always. Because my team is reasonably large, we put an emphasis on encapsulation and defensive coding (in the sense that code I write should be hard to use incorrectly). In my personal projects, I follow the same practice because it makes my code consistent across different projects, making it easy for an engineer that is familiar with my work to jump into another project of mine. It also encourages a kind of "mental muscle memory" so to speak, so it doesn't cost me any time when I'm creating a new variable or inspector parameter.
     
  18. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
    So, what do I have?
    And a tutorial: C# Serialization & Deserialization with Example that reminds me of another tutorial I found, about making a save system in Unity.

    It's better than nothing I guess! :D

    By the way, the tutorial about
    is behind a pay wall: Saving Persistent Player Data In Unity.
     
    Last edited: Jan 16, 2020 at 5:43 PM
  19. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,429
    Yeah, Unity now allows third parties to make paid tutorials that's put on Learn. That one goes over JsonUtility and PlayerPrefs - super simple stuff that there's probably a Brackeys tutorial on youtube for that's just as good.

    That's also a different concern again. When talking about [SerializeField] and friends, that's for serializing data in the scene, or on prefabs, or whatever. When talking about persistent player data, that's stuff like save games and settings and such.

    They're related, because both are about turning code objects into things that go into a text file, but you use different techniques.
     
    APSchmidt likes this.
  20. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
    That's what I came to thinking. I'm still not sure what to do though. :p
     
  21. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,429
    I'm not sure what you're not sure about!
     
  22. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    282
    I agree with @csofranz in that you should probably avoid using SerializeField if you're making basic tutorials for absolute Unity beginners to avoid overwhelming them too early on.

    If you're making a more advanced tutorial on the other hand, purposefully avoiding using non-public fields for serialization (or inspector-exposing) purposes I think would hurt more than it would help. It could teach bad practices that result in more buggy code, and indeed probably even make the code harder to read.

    Perhaps a simple example might help explain some of the benefits of using non-public fields.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Move : MonoBehaviour
    4. {
    5.     public Transform target;
    6.     public bool hasTarget;
    7.  
    8.     public void MoveTo(Transform moveToTarget)
    9.     {
    10.         target = moveToTarget;
    11.         hasTarget = moveToTarget != null;
    12.     }
    13.  
    14.     void Update()
    15.     {
    16.         if(!hasTarget)
    17.         {
    18.             return;
    19.         }
    20.         transform.position = Vector3.MoveTowards(transform.position, target.position, Time.deltaTime);
    21.     }
    22. }
    The above code has a problem. The target and hasTarget fields always need to be kept in sync; if hasTarget is set to true but target is still null, then the Update method will throw an expection.

    The issue can be effectively fixed by making the fields private, since then the only way that other classes can change the values of the fields is through the MoveTo method.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Move : MonoBehaviour
    4. {
    5.     [SerializeField]
    6.     private Transform target;
    7.  
    8.     [SerializeField, HideInInspector]
    9.     private bool hasTarget;
    10.  
    11.     public void MoveTo(Transform moveToTarget)
    12.     {
    13.         target = moveToTarget;
    14.         hasTarget = moveToTarget != null;
    15.     }
    16.  
    17.     private void Update()
    18.     {
    19.         if(!hasTarget)
    20.         {
    21.             return;
    22.         }
    23.         transform.position = Vector3.MoveTowards(transform.position, target.position, Time.deltaTime);
    24.     }
    25.  
    26.     // Also handle situation where values are changed through the inspector.
    27.     private void OnValidate()
    28.     {
    29.         hasTarget = target != null;
    30.     }
    31. }
    The readability of the code is also improved due to the access modifiers providing additional information about which members outside classes are allowed to access. Anyone reading the code can immediately see that the method MoveTo is the only one that they should care about in the context of communicating with this class from another one. They don't have to spend time thinking about whether or not altering the fields directly is supported or not.

    I personally wouldn't use SerializeField with public member fields. It's such common knowledge that public fields are serialized by Unity by default that I don't feel like it would add any value at this point.
     
  23. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
    Thank you for your contribution. :)

    I don't make tutorials so, I think all the posts related to tutorials here are directed @mbaske

    Generally speaking, I prefer using private fields or local variables. It's not always possible though.

    Me neither. I don't see why someone would do that.
     
  24. Inxentas

    Inxentas

    Joined:
    Jan 15, 2020
    Posts:
    40
    I rolled into Unity with a background in Object Oriented Programming. Access modifiers are novice level OOP concepts, while SerializeField is a Unity specific attribute. Telling people to use one of these over the other seems rather strange to me, since all they have in common is that they revolve around access.

    They are not actually things you can use "instead" of another.

    It is better to understand access modifiers and use SerializeField anyway, then it is to use SerializeField because you don't understand access modifiers. Oh, and never listen to people who tell you to always use things instead of other things. A knowledgeable teacher could come up with a case where the other thing is actually the superior solution.
     
  25. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
    I'm not sure that this
    Code (CSharp):
    1.     public bool instantiated;
    2.     public GameObject player;
    3.  
    4.     [SerializeField] private GameObject playerAnnaleePrefab, playerAshPrefab;
    5.  
    6.     private static GameManager gameManager;
    7.  
    8.     private bool isGameOver;
    9.     private SceneFader sceneFader;
    10.     private Canvas uiMenu;
    is better than this
    Code (CSharp):
    1.     public bool instantiated;
    2.     public GameObject player;
    3.  
    4.     public GameObject playerAnnaleePrefab, playerAshPrefab;
    5.  
    6.     private static GameManager gameManager;
    7.  
    8.     private bool isGameOver;
    9.     private SceneFader sceneFader;
    10.     private Canvas uiMenu;
    And for each [Serialize Field] variable, I get a warning that reads " Field '...' is never assigned to, and will always have its default value null". I know that it's only a warning, it still makes things unclear.

    Well, I see an advantage in using [Serialize Field]: it makes more visible at first sight which variables must be accessed from other scripts and which must not...

    A good reason to use it, eventually. :)

    Thank you for all your contributions. This thread didn't start a war and it's good. Visitors can keep sharing their feelings on the subject.
     
    Last edited: Jan 17, 2020 at 2:15 PM
  26. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    3,176
    This thread would not exist if I did that. :)
     
  27. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,404
    They are mostly useful if your code will be used by others, or you need a property to do/check something before setting a value.

    Code (csharp):
    1. public float speed;
    2.  
    3. public float Speed
    4. {
    5.     get => speed;
    6.     set
    7.     {
    8.         if (value < 0f)
    9.             speed = 0f;
    10.         else
    11.             speed = value;
    12.     }
    13. }
    14.  
    15. // In another script.....
    16.  
    17. void Update()
    18. {
    19.     scriptReference.speed -= 10 * Time.deltaTime; // Could bug your game. Will you notice?
    20.     scriptReference.Speed -= 10 * Time.deltaTime; // Works fine
    21. }
    22.  
    Making 'speed' private forces you to use 'Speed' instead. SerializeField allows you to see it in the inspector for prototyping.

    I usually keep most references public, like 'public GameObject particlePrefab'. I like them to have properties if accessed from outside.
     
    Last edited: Jan 17, 2020 at 3:11 PM
unityunity