3

Note: This is all being done in Unity. I haven't attempted to replicate it in a regular C# console app.

I'm running into an issue that I can't seem to understand. When I apply the System.Serializable attribute to the below TestingSerializable class, it seems to force sides = 0 instead of what it should actually be per the below code, sides = 6.

I know this because when I run the method PrintValue from within the Manager class below, the printed value will be zero instead of six. The only way I've found around this is to declare private const int sides = 6. This will keep it at 6 but I don't completely understand why. I'm assuming it has something to do with the fact that adding the const access modifier results in the value for 6 being baked into the code.

Mind you, this issue only occurs while running in Editor mode in Unity. When in Game mode it seems to work. This may also be the source of the issue but I'm not entirely sure why. I would like to continue running this in Editor mode, so I would appreciate solutions that allow me to keep operations in the Editor.

Can someone further explain why sides goes to zero when in a System.Serializable class unless it has a const access modifier?

EDIT 1: Added in Manager class to show how the TestingSerializable class is being utilized per the comments on this post.

//  Manager class to utilize the TestingSerializable class
public class Manager: MonoBehaviour
{
    [SerializeField, HideInInspector] private TestingSerializable testingSerializable;

    [SerializeField] [Range(2, 256)] private int resolution = 10;
    [SerializeField] [Range(1, 10)] private int radius= 5;

    private void OnValidate()
    {
        if (testingSerializable== null)
        {
            testingSerializable= new TestingSerializable(resolution, radius);
        }

        testingSerializable.PrintValue();
    }
}
[System.Serializable]
public class TestingSerializable
{
    private int resolution;
    private int radius;

    private int sides = 6;
    
    //  Constructor
    public TestingSerializable(int resolution, int radius)
    {
        this.resolution = resolution;
        this.radius = radius;
    }

    //  Method to print the value `sides`
    public void PrintValue()
    {
        Debug.Log(sides);
    }
}
Jee
  • 969
  • 1
  • 7
  • 26
  • 3
    "I know this because when I run the method PrintValue below" - on what object? An object that's been created via deserialization, or via calling the constructor? There'd be a *big* difference between those. – Jon Skeet Oct 09 '20 at 07:07
  • 1
    (Note that using the `const` modifier means it's no longer part of the state of the object at all.) – Jon Skeet Oct 09 '20 at 07:10
  • Does this answer your question? [C# serialize private class member](https://stackoverflow.com/questions/4314982/c-sharp-serialize-private-class-member) – GSerg Oct 09 '20 at 07:11
  • 1
    @JonSkeet would it? Shouldn't in both cases the value be assigned from the static assignment? Since it is private nothing will be serialized here anyway ... – derHugo Oct 09 '20 at 07:42
  • 1
    @derHugo: I *believe* binary deserialization goes via a different route, not calling a constructor *at all* unless one with a specific signature is provided. As the initialization is effectively inlined into the constructor (I believe), I suspect that's skipped during deserialization. (And private fields are definitely serialized with the binary formatter.) – Jon Skeet Oct 09 '20 at 08:51
  • @Jee could you add the code where and how you are using this `TestingSerializable` class exactly? – derHugo Oct 09 '20 at 08:53
  • @JonSkeet I think it is simply quite hard to say without knowing how OP uses this class. As I see it in order to deserialize from a binary you would need to serialize it first ... but none of the shown fields are actually serialized so afaik they should all have there default values from the class definition and not be touched by the serializer at all ;) Since they are all private they would also not be changed from anywhere else ... unless of course and (I'ld suspect) that OP didn't show us a minimal but valid code example – derHugo Oct 09 '20 at 08:55
  • @derHugo: Again, I believe the binary serializer *does* serialize all fields, including private ones. From https://learn.microsoft.com/en-us/dotnet/standard/serialization/binary-serialization: "During this process, the public and private fields of the object and the name of the class, including the assembly containing the class, are converted to a stream of bytes, which is then written to a data stream." – Jon Skeet Oct 09 '20 at 09:14
  • @JonSkeet oh damn that I didn't know! Thanks! Still though: I doubt this is the full code .. since in order to serialize this thing it either once has to have been `0` when it was serialized before or should serialize the `6` since the value is nowhere changed ^^ – derHugo Oct 09 '20 at 09:20
  • @Jee is it possible that your serialized this once before when this field was still `0`? – derHugo Oct 09 '20 at 09:26
  • @derHugo: My *guess* is that the OP is deserializing data from a version when the field didn't exist at all. A [mcve] would really help... – Jon Skeet Oct 09 '20 at 09:41
  • @JonSkeet afaik if the field didn't exist then the BinaryFormatter would fail and throw an exception. But yes anything further from here will just be wild speculations ;) – derHugo Oct 09 '20 at 09:47
  • @derHugo: No, I believe you can add a field later on, and it will just end up with the type's default value. Will see if I can reproduce that now... – Jon Skeet Oct 09 '20 at 10:20
  • @JonSkeet see e.g. [binary serialization, adding a new field to class - will it work?](https://stackoverflow.com/questions/3527399/binary-serialization-adding-a-new-field-to-class-will-it-work) – derHugo Oct 09 '20 at 10:27
  • @derHugo: Ah, that's interesting. Won't bother trying to repro then :) – Jon Skeet Oct 09 '20 at 10:28
  • @JonSkeet I edited the question to further clarify the usage. This is in Unity and I'm calling it from the OnValidate function while in Editor Mode. Works in Game mode though so I'm thinking the issue has more to do with being in Editor mode now. – Jee Oct 09 '20 at 17:22
  • @derHugo see above comment. Apparently StackOverflow only allows me to tag one person per comment. Never realized that. – Jee Oct 09 '20 at 17:23
  • @JonSkeet Any thoughts? – Jee Oct 19 '20 at 03:17
  • @derHugo Any thoughts? – Jee Oct 19 '20 at 03:17
  • @Jee question is the same as before: Did this field exist before with a different value? Or was it added afterwards? And still: None of these values will be saved since they are private and therefore not serialized! Since the class itself is serialized though since the field `testingSerializable` where you store it is `[SerializeField]` the next time it is probably not `null` and never will be until you reset it ... The constructor will not be called again and `sides` (nor the others) not initialized. All three fields will probably always have the value `0` since they are not serialized – derHugo Oct 19 '20 at 05:34
  • That's moved from "we don't know when TestSerializable is being deserialized" to "we don't know when Manager is being deserialized". – Jon Skeet Oct 19 '20 at 06:20
  • @derHugo: As before, I still believe private fields *are* serialized using default binary serialization. Do you have a reason to believe that Unity binary serialization doesn't serialize private fields? – Jon Skeet Oct 19 '20 at 06:22
  • @JonSkeet yes! See [Unity Script Serialization](https://docs.unity3d.com/Manual/script-Serialization.html) -> `Serialization rules: ... serialization in Unity behaves differently to serialization in other programming environments. The following section outlines how to use serialization in Unity. To use field serialization you must ensure it: Is public, or has a SerializeField attribute; Is not static; Is not const; Is not readonly; Has a fieldtype that can be serialized. (See Simple field types that can be serialized, below.)` – derHugo Oct 19 '20 at 08:03
  • @derHugo: Aha - I think that would have been good to bring up earlier, as it sounds like that's basically the key :) – Jon Skeet Oct 19 '20 at 08:31
  • @JonSkeet hehe yeah .. just your link confused me and yes it was necessary to know first how OP actually uses and serializes it .. if a `BinaryFormatter` was used then yours would still apply – derHugo Oct 19 '20 at 10:52

0 Answers0