浏览代码

Serialization and more documentation on attributes

Eideren 2 年之前
父节点
当前提交
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.
 You can attach the same script to multiple entities and set different property values on each entity.
 
 
 > [!Note] 
 > [!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
 ## 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)
 ![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
 ## See also
 
 
+* [Serialization](serialization.md)
 * [Types of script](types-of-script.md)
 * [Types of script](types-of-script.md)
 * [Create a script](create-a-script.md)
 * [Create a script](create-a-script.md)
 * [Use a script](use-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]`                    |
 | `[Serializable]`          | `[DataContract]`                    |
 | `[SerializeField]`        | `[DataMember]`                      |
 | `[SerializeField]`        | `[DataMember]`                      |
 | `[HideInInspector]`       | `[DataMemberIgnore]`                |
 | `[HideInInspector]`       | `[DataMemberIgnore]`                |
+| `[Range]`                 | `[DataMemberRange]`                 |
 | `[Header]`                | `[Display]`                         |
 | `[Header]`                | `[Display]`                         |
 | `[Tooltip("My tooltip")]` | `/// <userdoc>My tooltip</userdoc>` |
 | `[Tooltip("My tooltip")]` | `/// <userdoc>My tooltip</userdoc>` |