Browse Source

Serialization and more documentation on attributes

Eideren 2 years ago
parent
commit
39226f0c58

+ 16 - 1
en/manual/scripts/public-properties-and-fields.md

@@ -10,7 +10,7 @@ When you declare a public property or field in a script, the property becomes ac
 You can attach the same script to multiple entities and set different property values on each entity.
 
 > [!Note] 
-> Public properties or fields must be serializable to be used in Game Studio. 
+> Properties and fields must be [serializable](serialization.md) to be used in Game Studio. 
 
 ## Add a public property or field
 
@@ -100,8 +100,23 @@ On next reload, the Game Studio should display the documentation:
 
 ![The description now shows in the Property Grid](media/userdoc-example.png)
 
+## [MemberRequiredAttribute](xref:Stride.Core.Annotations.MemberRequiredAttribute)
+This attribute is used to specify if a field or property should not be left null in editor.
+If no values are set for this member, a warning or error will be logged when building your game.
+```cs
+[Stride.Core.Annotations.MemberRequired(MemberRequiredReportType.Error)] public CharacterComponent MyCharacter;
+```
+
+#### [DataMemberRangeAttribute](xref:Stride.Core.Annotations.DataMemberRangeAttribute)
+#### [InlinePropertyAttribute](xref:Stride.Core.Annotations.InlinePropertyAttribute)
+#### [ItemCanBeNullAttribute](xref:Stride.Core.Annotations.ItemCanBeNullAttribute)
+#### [ItemNotNullAttribute](xref:Stride.Core.Annotations.ItemNotNullAttribute)
+#### [MemberCollectionAttribute](xref:Stride.Core.Annotations.MemberCollectionAttribute)
+#### [DataStyleAttribute](xref:Stride.Core.DataStyleAttribute)
+
 ## See also
 
+* [Serialization](serialization.md)
 * [Types of script](types-of-script.md)
 * [Create a script](create-a-script.md)
 * [Use a script](use-a-script.md)

+ 149 - 0
en/manual/scripts/serialization.md

@@ -0,0 +1,149 @@
+# Serialization
+
+<span class="label label-doc-level">Beginner</span>
+<span class="label label-doc-audience">Programmer</span>
+
+The editor and serialization system uses three attributes to determine what is serialized and visible in the editor.
+
+### [DataContractAttribute](xref:Stride.Core.DataContractAttribute)
+Adding this attribute to your `class` or `struct` notifies the serializer and the editor that it should
+show fields and properties of that type, and serialize the data it contains with the scenes or assets that might include it.
+```cs
+[DataContract(Inherited = true)]
+public class MySerializedClass
+{
+    public float MyValue;
+}
+
+// 'DataContract' is inherited above. You don't need to specify it for a derived class.
+public class MyDerivedSerializedClass : MySerializedClass
+{
+    public string MyName;
+}
+```
+
+> [!Note]
+> Your IDE may wrongfully add `System.Runtime.Serialization` to your list of `using` when adding `DataContract`.
+> They are not interchangeable.
+> Make sure that your `DataContract` comes from `Stride.Core`, specifying the namespace explicitly like shown above if necessary.
+
+### [DataMemberAttribute](xref:Stride.Core.DataMemberAttribute)
+This notifies the editor and serializer that the property or field on this [DataContract](#datacontractattribute)'ed
+`class` or `struct` should be serialized.
+Note that you can omit this attribute for most public fields and properties, they will be included by default,
+see [Fields](#fields) and [Properties](#properties) for specifics.
+```cs
+[Stride.Core.DataContract]
+public class MySerializedClass
+{
+    [Stride.Core.DataMember]
+    internal float MyValue;
+    
+    
+    [DataMember("Item1")]
+    public string ItemRenamed1 { get; set; }
+}
+```
+
+### [DataAliasAttribute](xref:Stride.Core.DataAliasAttribute)
+Can be used to ensure you do not break previously serialized data whenever you have to change how that member is named in your source.
+```cs
+[Stride.Core.DataAlias("PreviousNameOfProp")]
+public string MyRenamedProp { get; set; }
+```
+
+### [DataMemberIgnoreAttribute](xref:Stride.Core.DataMemberIgnoreAttribute)
+This notifies the editor and serializer that the property or field on this [DataContract](#datacontractattribute)'ed
+`class` or `struct` should ***NOT*** be serialized.
+```cs
+[Stride.Core.DataContract]
+public class MySerializedClass
+{
+    [Stride.Core.DataMemberIgnore]
+    public float MyValue { get; set; } // This public property will NOT show up in the editor
+}
+```
+
+#### TODO
+- [DataMemberCustomSerializerAttribute](xref:Stride.Core.DataMemberCustomSerializerAttribute)
+- [DataMemberUpdatableAttribute](xref:Stride.Updater.DataMemberUpdatableAttribute)
+
+## Rule of Thumb
+Serialization and the editor's access and view of your properties mirrors how access modifiers work in C#;
+
+Think of the serializer/editor as being a class external to your codebase, if you want the serializer to
+read and write your properties you have to ensure that the access modifiers for its getter and setter
+allows the serializer to access them.
+
+If you're hiding that property behind an `internal` access modifier, you have to annotate it with
+the attribute to ensure it is visible to the serializer.
+
+
+## Fields
+
+```cs
+// Read and set in the editor by default
+public object obj;
+
+// Read and set in editor with attribute
+[DataMember] public internal object obj;
+
+// Read only ... readonly are read only.
+public readonly object obj;
+[DataMember] internal readonly object obj;
+
+// Never
+private protected/private/protected object obj;
+```
+
+
+## Properties
+
+```cs
+// Read and set in the editor ...
+
+// By default
+public object obj { get; set; }
+public object obj { get => x; set => x = value; }
+
+// Forced with the attribute for 'internal' modifiers
+[DataMember] public object obj { internal get; public/internal set; }
+[DataMember] public object obj { internal get => x; public/internal set => x; }
+
+// Read only
+public object obj { get; }
+
+// Read only for any non-public access modifier
+public object obj { get; internal/private protected/private/protected set; }
+public object obj { get => x; internal/private protected/private/protected set => x = value; }
+
+// Read only for internal properties must be enforced through the attribute
+[DataMember] internal object obj { get; }
+[DataMember] internal object obj { get => x; }
+
+// Read only, special case for get-only public properties without backing field, 
+//They must use the attribute to be deserialized, see the comment below
+[DataMember] public object obj { get => x; }
+
+// Read only for access modifiers more restrictive than internal, even with the attribute
+[DataMember] public object obj { internal get; private protected/private/protected set; }
+[DataMember] public object obj { internal get => x; private protected/private/protected set => x; }
+
+// Never
+private protected/private/protected object obj { get; set; }
+private protected/private/protected object obj { get => x; set => x; }
+```
+
+> [!Note]
+> Get-only public properties without backing field (`public object obj { get => x; }`) are not serialized by default as
+> they are, more often than not, shortcuts to values of another object or used purely as a function.
+> It might make more sense to change it to `{ get; } = new MyObject();` or `{ get; init; }` if you want to serialize it,
+> and if that doesn't work for you, feel free to add the attribute to enforce serialization.
+
+### What about [init](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/init) ?
+The `init` access modifier is seen as a `public set` by the editor and serialization, it will be settable in the editor.
+
+
+## See also
+
+* [Public properties and fields](public-properties-and-fields.md)

+ 1 - 0
en/manual/stride-for-unity-developers/index.md

@@ -787,6 +787,7 @@ System.Diagnostics.Debug.WriteLine("hello");
 | `[Serializable]`          | `[DataContract]`                    |
 | `[SerializeField]`        | `[DataMember]`                      |
 | `[HideInInspector]`       | `[DataMemberIgnore]`                |
+| `[Range]`                 | `[DataMemberRange]`                 |
 | `[Header]`                | `[Display]`                         |
 | `[Tooltip("My tooltip")]` | `/// <userdoc>My tooltip</userdoc>` |