Skip to content

Commit fc2b74a

Browse files
committed
反射构造接口的实现类时将会尝试调用无参构造函数
1 parent f9616bf commit fc2b74a

File tree

2 files changed

+92
-100
lines changed

2 files changed

+92
-100
lines changed

Assets/Scripts/SerializeExtension/Editor/SerializeExtensionDrawer.cs

Lines changed: 84 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,98 @@
1-
using System.Collections.Generic;
21
using UnityEditor;
32
using UnityEngine;
43
using System;
54
using System.Linq;
6-
using System.Runtime.Serialization;
75
using System.Reflection;
8-
using System.Text.RegularExpressions;
96
using System.Collections;
7+
using System.Collections.Generic;
8+
using System.Runtime.Serialization;
9+
using System.Text.RegularExpressions;
1010

1111
namespace Core
1212
{
1313
[CustomPropertyDrawer(typeof(SerializeExtensionAttribute))]
1414
public class SerializeExtensionDrawer : PropertyDrawer
1515
{
16-
private static LogicAndStack _guiEnableStack = new();
16+
private static readonly LogicAndStack _guiEnableStack = new();
1717

1818
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
1919
return property.isExpanded ? EditorGUI.GetPropertyHeight(property, true) : EditorGUIUtility.singleLineHeight;
2020
}
2121

2222
private new SerializeExtensionAttribute attribute => (SerializeExtensionAttribute)base.attribute;
2323

24-
2524
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
26-
if(attribute.NameInEditorWindow != null) {
27-
label.text = attribute.NameInEditorWindow;
28-
}
29-
if(attribute.ToolTips != null) {
30-
label.tooltip = attribute.ToolTips;
31-
}
32-
33-
Type fieldType = null;
34-
bool isUnityObject = typeof(UnityEngine.Object).IsAssignableFrom(fieldInfo.FieldType);
35-
if (isUnityObject || fieldInfo.FieldType.IsValueType || property.propertyType != SerializedPropertyType.ManagedReference) {
36-
fieldType = fieldInfo.FieldType;
37-
}
38-
else {
39-
string[] info = property.managedReferenceFieldTypename.Split();
40-
string asseblyName = info[0], typeName = info[1];
41-
Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(item => item.GetName().Name == asseblyName);
42-
fieldType = assembly.GetType(typeName);
43-
}
44-
45-
var owner = GetFieldOwner(property);
46-
PropertyInfo proxy = null;
47-
bool proxyIsValid = false;
48-
if (attribute.ProxyPropertyName != null && (fieldType.IsValueType || isUnityObject)) {
49-
proxy = owner.GetType().GetProperty(attribute.ProxyPropertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
50-
proxyIsValid = CheckPropertyProxyValid(proxy, owner, fieldType);
51-
}
25+
label.text = attribute.NameInEditorWindow ?? label.text;
26+
label.tooltip = attribute.ToolTips ?? label.tooltip;
5227

5328
GUI.enabled = _guiEnableStack.Push(attribute.CanWrite);
5429
EditorGUI.BeginProperty(position, label, property);
5530
EditorGUI.BeginChangeCheck();
31+
32+
Type fieldType = fieldInfo.FieldType;
5633
if (fieldType.IsAbstract || fieldType.IsInterface) {
57-
ShowPolymorphismField(fieldType, position, property, label);
34+
ShowPolymorphismField(position, property, label);
5835
}
5936
else {
6037
EditorGUI.PropertyField(position, property, label, true);
6138
}
6239

6340
bool hasValueUpdated = EditorGUI.EndChangeCheck() || !attribute.CanWrite; // !attribute.CanWrite : 当变量不可写时大概率用作显示某个字段的成员信息,为了避免字段发生修改时成员信息更新不同步的问题需要保持更新才行
64-
if (proxyIsValid && hasValueUpdated) {
65-
property.serializedObject.ApplyModifiedProperties();
66-
if (proxy.CanRead && proxy.CanWrite) {
67-
proxy.SetValue(owner, proxy.GetValue(owner));
68-
}
69-
else if (proxy.CanRead) {
70-
fieldInfo.SetValue(owner, proxy.GetValue(owner));
71-
}
72-
else if (proxy.CanWrite) {
73-
proxy.SetValue(owner, fieldInfo.GetValue(owner));
41+
bool canSerializeType = typeof(UnityEngine.Object).IsAssignableFrom(fieldType) || fieldType.IsValueType;
42+
if (hasValueUpdated && canSerializeType && attribute.ProxyPropertyName != null) {
43+
var owner = GetFieldOwner(property);
44+
PropertyInfo proxy = owner.GetType().GetProperty(attribute.ProxyPropertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); ;
45+
if (CheckPropertyProxyValid(proxy, owner, fieldType)) {
46+
property.serializedObject.ApplyModifiedProperties();
47+
if (proxy.CanRead && proxy.CanWrite) {
48+
proxy.SetValue(owner, proxy.GetValue(owner));
49+
}
50+
else if (proxy.CanRead) {
51+
fieldInfo.SetValue(owner, proxy.GetValue(owner));
52+
}
53+
else if (proxy.CanWrite) {
54+
proxy.SetValue(owner, fieldInfo.GetValue(owner));
55+
}
7456
}
7557
}
58+
7659
GUI.enabled = _guiEnableStack.Pop();
7760
EditorGUI.EndProperty();
7861
}
7962

8063
#region Show Polymorphism Field
8164
private readonly static Dictionary<Type, List<Type>> _subTypeDict = new();
8265

83-
private void ShowPolymorphismField(Type abstractType, Rect position, SerializedProperty property, GUIContent label) {
66+
private void ShowPolymorphismField(Rect position, SerializedProperty property, GUIContent label) {
67+
// 绘制选择框
68+
Type abstractType = fieldInfo.FieldType;
69+
List<Type> subTypes = GetSubTypes(abstractType);
70+
8471
Rect labelRect = EditorGUI.IndentedRect(new(position) {
8572
height = EditorGUIUtility.singleLineHeight
8673
});
8774
Rect popupRect = EditorGUI.PrefixLabel(labelRect, label);
88-
if (attribute.CanSwitchSubType) {
89-
var subTypes = GetSubTypes(abstractType);
9075

91-
var fieldType = property.managedReferenceValue?.GetType();
92-
int currentIndex = subTypes.FindIndex(type => type == fieldType) + 1; // 会占用选择框0号位置为"None (null)" 所以下标会加一个偏移量
93-
string[] selectBoxText = subTypes.Select(type => type.Name).Prepend("None (null)").ToArray();
76+
string[] selectBoxTexts = subTypes.Select(type => type.Name).Prepend("None (null)").ToArray();
77+
Type fieldType = property.managedReferenceValue?.GetType();
78+
int currentIndex = subTypes.FindIndex(type => type == fieldType) + 1; // 选择框会占用0号位置为"None (null)" 所以下标会加一个偏移量
9479

95-
int newSelectIndex = EditorGUI.Popup(popupRect, currentIndex, selectBoxText);
96-
if (newSelectIndex != currentIndex) {
97-
if (newSelectIndex == 0) {
98-
property.managedReferenceValue = null;
99-
}
100-
else {
101-
bool hasDefaultConstructor = false;
102-
103-
Type type = subTypes[newSelectIndex - 1]; // 将选择框的一个偏移量减回来
104-
ConstructorInfo[] constructors = type.GetConstructors();
105-
// 查找无参构造函数
106-
foreach (var constructor in constructors) {
107-
if (constructor.GetParameters().Length == 0) {
108-
// 调用无参构造函数并返回实例
109-
hasDefaultConstructor = true;
110-
property.managedReferenceValue = constructor.Invoke(null);
111-
}
112-
}
113-
114-
if (!hasDefaultConstructor) {
115-
property.managedReferenceValue = FormatterServices.GetSafeUninitializedObject(type);
116-
}
117-
}
118-
property.serializedObject.ApplyModifiedProperties(); //更改多态类型后必须马上保存,否则后续序列化可能会出现异常
80+
GUI.enabled = attribute.CanSwitchSubType;
81+
int newSelectIndex = EditorGUI.Popup(popupRect, currentIndex, selectBoxTexts);
82+
GUI.enabled = true;
83+
84+
if (newSelectIndex != currentIndex) {
85+
if (newSelectIndex == 0) {
86+
property.managedReferenceValue = null;
87+
}
88+
else {
89+
Type type = subTypes[newSelectIndex - 1]; // 将选择框的一个偏移量减回来
90+
property.managedReferenceValue = CreateInstance(type);
11991
}
92+
property.serializedObject.ApplyModifiedProperties(); //更改多态类型后必须马上保存,否则后续序列化可能会出现异常
12093
}
12194

95+
// 绘制序列化字段
12296
Rect foldoutRect = new(position) {
12397
height = EditorGUIUtility.singleLineHeight
12498
};
@@ -135,30 +109,50 @@ private void ShowPolymorphismField(Type abstractType, Rect position, SerializedP
135109
}
136110
}
137111
}
112+
}
138113

139-
static List<Type> GetSubTypes(Type abstractType) {
140-
if (!_subTypeDict.TryGetValue(abstractType, out var subTypes)) {
141-
subTypes = AppDomain.CurrentDomain.GetAssemblies()
142-
.SelectMany(assembly => assembly.GetTypes())
143-
.Where(type => abstractType.IsAssignableFrom(type) && !type.IsAbstract && !type.IsInterface)
144-
.ToList();
114+
private List<Type> GetSubTypes(Type abstractType) {
115+
if (!_subTypeDict.TryGetValue(abstractType, out var subTypes)) {
116+
subTypes = AppDomain.CurrentDomain.GetAssemblies()
117+
.SelectMany(assembly => assembly.GetTypes())
118+
.Where(type => abstractType.IsAssignableFrom(type) && !type.IsAbstract && !type.IsInterface)
119+
.ToList();
145120

146-
const int maxCacheSize = 10;
147-
while (_subTypeDict.Count > maxCacheSize) {
148-
_subTypeDict.Remove(_subTypeDict.Keys.First());
149-
}
150-
_subTypeDict.Add(abstractType, subTypes);
121+
const int maxCacheSize = 10;
122+
while (_subTypeDict.Count > maxCacheSize) {
123+
_subTypeDict.Remove(_subTypeDict.Keys.First());
151124
}
152-
return subTypes;
125+
_subTypeDict.Add(abstractType, subTypes);
153126
}
127+
return subTypes;
128+
}
154129

155-
//void SetPropertymanagedReferenceValue(List<Type> subTypes, int selectIndex) {
156-
// if (selectIndex == curIndex || selectIndex < 0 || selectIndex >= subTypes.Count) return;
157-
// property.managedReferenceValue = FormatterServices.GetSafeUninitializedObject(subTypes[selectIndex]);
158-
// curIndex = selectIndex;
159-
// property.serializedObject.ApplyModifiedProperties(); //更改多态类型后必须马上保存,否则后续序列化可能会出现异常
160-
//}
130+
/// <summary>
131+
/// 尝试使用无参构造函数创造实例,若失败则返回未初始化类型
132+
/// </summary>
133+
private object CreateInstance(Type type) {
134+
foreach (var constructor in type.GetConstructors()) {
135+
if (constructor.GetParameters().Length == 0) {
136+
return constructor.Invoke(null);
137+
}
138+
}
139+
return FormatterServices.GetSafeUninitializedObject(type);
161140
}
141+
142+
//private Type GetRuntimeType(SerializedProperty property) {
143+
// Type fieldType = fieldInfo.FieldType;
144+
// bool canSerializeType = typeof(UnityEngine.Object).IsAssignableFrom(fieldType) || fieldType.IsValueType;
145+
// if (canSerializeType || property.propertyType != SerializedPropertyType.ManagedReference) {
146+
// return fieldInfo.FieldType;
147+
// }
148+
// else {
149+
// string[] info = property.managedReferenceFieldTypename.Split();
150+
// string asseblyName = info[0], typeName = info[1];
151+
// Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(item => item.GetName().Name == asseblyName);
152+
// return assembly.GetType(typeName);
153+
// }
154+
//}
155+
162156
#endregion
163157

164158
private object GetFieldOwner(SerializedProperty property) {

Assets/Scripts/Test/Test.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,8 @@
44

55
public class Test : MonoBehaviour
66
{
7-
//[SerializeExtension]
8-
//[SerializeReference] IPerson peopel;
9-
10-
//[SerializeExtension(proxyPropertyName: nameof(Health))]
11-
//[SerializeField] int health;
12-
13-
//public int Health {
14-
// get => health;
15-
// set => health = Mathf.Clamp(value, 0, 100);
16-
//}
7+
[SerializeExtension(nameInEditorWindow: "血量", proxyPropertyName: nameof(Health))]
8+
[SerializeField] int health;
179

1810
[SerializeExtension]
1911
[SerializeReference] IPerson peopel;
@@ -23,4 +15,10 @@ public class Test : MonoBehaviour
2315

2416
[SerializeExtension(canWrite: false)]
2517
[SerializeField] int b = 20;
18+
19+
public int Health {
20+
get => health;
21+
set => health = Mathf.Clamp(value, 0, 100);
22+
}
23+
2624
}

0 commit comments

Comments
 (0)