SerializeField vs public (and vice versa)

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

1 Like

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…

4 Likes

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.

5 Likes

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. :wink:

What do you mean by “subclassing scripts”; I don’t think I had heard of that before?

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

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:

class anotherOfMine : myClass

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.

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.

Ah, all right, I knew about that, I just didn’t connect to “subclass”. :slight_smile:

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. :slight_smile:

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.

1 Like

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 :slight_smile: – “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 :slight_smile:

What about protected? Any difference with [Serialize Field]?

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?

Protected, of course.

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.

Okay, what difference is there between the two of them? Is a serialised field not accessible from child classes?

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.

1 Like

I hadn’t noticed that thread: When to use [SerializeField] and why?

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.

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! :smile:

By the way, the tutorial about

is behind a pay wall: Saving Persistent Player Data In Unity.

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.

1 Like

That’s what I came to thinking. I’m still not sure what to do though. :stuck_out_tongue: