Skip to content

Commit 72eaa6f

Browse files
committed
Better way to handle objects instantiation when using the ReferencePicker attribute; possibility to force uninitialized object creation
1 parent 71c855d commit 72eaa6f

File tree

4 files changed

+51
-16
lines changed

4 files changed

+51
-16
lines changed

Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#if UNITY_2019_3_OR_NEWER
22
using System;
3-
using System.Runtime.Serialization;
43

54
using UnityEditor;
65
using UnityEngine;
@@ -17,13 +16,12 @@ public class ReferencePickerAttributeDrawer : ToolboxSelfPropertyDrawer<Referenc
1716
private static readonly TypeAppearanceContext sharedAppearance = new TypeAppearanceContext(sharedConstraint, TypeGrouping.None, true);
1817
private static readonly TypeField typeField = new TypeField(sharedConstraint, sharedAppearance);
1918

20-
2119
private void UpdateContexts(ReferencePickerAttribute attribute)
2220
{
2321
sharedAppearance.TypeGrouping = attribute.TypeGrouping;
2422
}
2523

26-
private Type GetParentType(SerializedProperty property, ReferencePickerAttribute attribute)
24+
private Type GetParentType(ReferencePickerAttribute attribute, SerializedProperty property)
2725
{
2826
var fieldInfo = property.GetFieldInfo(out _);
2927
var fieldType = property.GetProperType(fieldInfo);
@@ -42,7 +40,7 @@ private Type GetParentType(SerializedProperty property, ReferencePickerAttribute
4240
return fieldType;
4341
}
4442

45-
private void CreateTypeProperty(Rect position, SerializedProperty property, Type parentType)
43+
private void CreateTypeProperty(SerializedProperty property, Type parentType, ReferencePickerAttribute attribute, Rect position)
4644
{
4745
TypeUtilities.TryGetTypeFromManagedReferenceFullTypeName(property.managedReferenceFullTypename, out var currentType);
4846
typeField.OnGui(position, true, (type) =>
@@ -51,7 +49,7 @@ private void CreateTypeProperty(Rect position, SerializedProperty property, Type
5149
{
5250
if (!property.serializedObject.isEditingMultipleObjects)
5351
{
54-
UpdateTypeProperty(property, type);
52+
UpdateTypeProperty(property, type, attribute);
5553
}
5654
else
5755
{
@@ -61,7 +59,7 @@ private void CreateTypeProperty(Rect position, SerializedProperty property, Type
6159
using (var so = new SerializedObject(target))
6260
{
6361
SerializedProperty sp = so.FindProperty(property.propertyPath);
64-
UpdateTypeProperty(sp, type);
62+
UpdateTypeProperty(sp, type, attribute);
6563
}
6664
}
6765
}
@@ -73,9 +71,10 @@ private void CreateTypeProperty(Rect position, SerializedProperty property, Type
7371
}, currentType, parentType);
7472
}
7573

76-
private void UpdateTypeProperty(SerializedProperty property, Type referenceType)
74+
private void UpdateTypeProperty(SerializedProperty property, Type targetType, ReferencePickerAttribute attribute)
7775
{
78-
var obj = referenceType != null ? FormatterServices.GetUninitializedObject(referenceType) : null;
76+
var forceUninitializedInstance = attribute.ForceUninitializedInstance;
77+
var obj = ReflectionUtility.CreateInstance(targetType, forceUninitializedInstance);
7978
property.serializedObject.Update();
8079
property.managedReferenceValue = obj;
8180
property.serializedObject.ApplyModifiedProperties();
@@ -106,7 +105,6 @@ private Rect PrepareTypePropertyPosition(bool hasLabel, in Rect labelPosition, i
106105
return position;
107106
}
108107

109-
110108
protected override void OnGuiSafe(SerializedProperty property, GUIContent label, ReferencePickerAttribute attribute)
111109
{
112110
//NOTE: we want to close scope manually because ExitGUIException can interrupt drawing and SerializedProperties stack
@@ -122,8 +120,8 @@ protected override void OnGuiSafe(SerializedProperty property, GUIContent label,
122120
var hasLabel = !string.IsNullOrEmpty(label.text);
123121
var position = PrepareTypePropertyPosition(hasLabel, in labelRect, in inputRect, isPropertyExpanded);
124122

125-
var parentType = GetParentType(property, attribute);
126-
CreateTypeProperty(position, property, parentType);
123+
var parentType = GetParentType(attribute, property);
124+
CreateTypeProperty(property, parentType, attribute, position);
127125
if (isPropertyExpanded)
128126
{
129127
ToolboxEditorGui.DrawPropertyChildren(property);
@@ -134,7 +132,6 @@ protected override void OnGuiSafe(SerializedProperty property, GUIContent label,
134132
}
135133
}
136134

137-
138135
public override bool IsPropertyValid(SerializedProperty property)
139136
{
140137
return property.propertyType == SerializedPropertyType.ManagedReference;

Assets/Editor Toolbox/Editor/Utilities/ReflectionUtility.cs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
using System;
22
using System.Reflection;
3-
3+
using System.Runtime.Serialization;
44
using UnityEditor;
55
using Object = UnityEngine.Object;
66

77
namespace Toolbox.Editor
88
{
99
internal static class ReflectionUtility
1010
{
11-
private readonly static Assembly editorAssembly = typeof(UnityEditor.Editor).Assembly;
11+
private static readonly Assembly editorAssembly = typeof(UnityEditor.Editor).Assembly;
1212

1313
public const BindingFlags allBindings = BindingFlags.Instance |
1414
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
1515

16-
1716
/// <summary>
1817
/// Returns <see cref="MethodInfo"/> of the searched method within the Editor <see cref="Assembly"/>.
1918
/// </summary>
@@ -96,5 +95,31 @@ internal static bool TryInvokeMethod(string methodName, SerializedObject seriali
9695

9796
return true;
9897
}
98+
99+
internal static object CreateInstance(Type targetType, bool forceUninitializedInstance)
100+
{
101+
if (targetType == null)
102+
{
103+
return null;
104+
}
105+
106+
if (forceUninitializedInstance)
107+
{
108+
return FormatterServices.GetUninitializedObject(targetType);
109+
}
110+
111+
if (targetType.IsValueType)
112+
{
113+
return Activator.CreateInstance(targetType);
114+
}
115+
116+
var defaultConstructor = targetType.GetConstructor(Type.EmptyTypes);
117+
if (defaultConstructor != null)
118+
{
119+
return Activator.CreateInstance(targetType);
120+
}
121+
122+
return FormatterServices.GetUninitializedObject(targetType);
123+
}
99124
}
100125
}

Assets/Editor Toolbox/Runtime/Attributes/Toolbox/PropertySelfAttributes/ReferencePickerAttribute.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public ReferencePickerAttribute(Type parentType, TypeGrouping typeGrouping)
3131
/// Defaults to <see cref="TypeGrouping.None"/> unless explicitly specified.
3232
/// </summary>
3333
public TypeGrouping TypeGrouping { get; set; } = TypeGrouping.None;
34+
35+
public bool ForceUninitializedInstance { get; set; }
3436
}
3537
}
3638
#endif

Assets/Examples/Scripts/SampleBehaviour6.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class SampleBehaviour6 : MonoBehaviour
99
#if UNITY_2019_3_OR_NEWER
1010
[SerializeReference, ReferencePicker(TypeGrouping = TypeGrouping.ByFlatName)]
1111
public Interface1 var1;
12-
[SerializeReference, ReferencePicker]
12+
[SerializeReference, ReferencePicker(ForceUninitializedInstance = true)]
1313
public ClassWithInterfaceBase var2;
1414
[SerializeReference, ReferencePicker(ParentType = typeof(ClassWithInterface2))]
1515
public ClassWithInterfaceBase var3;
@@ -25,6 +25,12 @@ public struct Struct : Interface1
2525
{
2626
public bool var1;
2727
public bool var2;
28+
29+
public Struct(bool var1, bool var2)
30+
{
31+
this.var1 = var1;
32+
this.var2 = var2;
33+
}
2834
}
2935

3036
public abstract class ClassWithInterfaceBase : Interface1
@@ -54,6 +60,11 @@ public class ClassWithInterface2 : ClassWithInterfaceBase
5460
public class ClassWithInterface3 : ClassWithInterfaceBase
5561
{
5662
public int var1;
63+
64+
public ClassWithInterface3(int var1)
65+
{
66+
this.var1 = var1;
67+
}
5768
}
5869

5970
[Serializable]

0 commit comments

Comments
 (0)