Use delegates for custom serializers

This commit is contained in:
Simon Lübeß
2024-01-05 14:34:11 +01:00
parent a5e767df01
commit 9da675e4bb
4 changed files with 43 additions and 16 deletions
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace GlitchyEngine.Extensions;
public static class MethodInfoExtension
{
/// <summary>
/// Gets the <see cref="Delegate"/> of this <see cref="MethodInfo"/>.
/// </summary>
/// <typeparam name="T">The type of the Delegate.</typeparam>
/// <param name="info">The method info.</param>
/// <returns>The delegate.</returns>
public static T GetDelegate<T>(this MethodInfo info) where T : Delegate
{
return (T)Delegate.CreateDelegate(typeof(T), info);
}
}
@@ -4,7 +4,7 @@ using System.Text;
namespace GlitchyEngine.Serialization; namespace GlitchyEngine.Serialization;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
public sealed class CustomSerializerAttribute : Attribute public sealed class CustomSerializerAttribute : Attribute
{ {
public Type Type { get; private set; } public Type Type { get; private set; }
@@ -40,8 +40,9 @@ public class DeserializationObject
private Dictionary<string, Type> _fullNameToType = new(); private Dictionary<string, Type> _fullNameToType = new();
private static Dictionary<Type, MethodInfo> _customDeserializers = new(); private delegate object? DeserializeMethod(DeserializationObject container, string fieldName, Type fieldType);
private static Dictionary<Type, DeserializeMethod> _customDeserializers = new();
/// <summary> /// <summary>
/// Gets the type that was originally stored in the container, or null, if the type doesn't exist. /// Gets the type that was originally stored in the container, or null, if the type doesn't exist.
@@ -69,7 +70,7 @@ public class DeserializationObject
{ {
if (type.TryGetCustomAttribute<CustomSerializerAttribute>(out var attribute)) if (type.TryGetCustomAttribute<CustomSerializerAttribute>(out var attribute))
{ {
MethodInfo deserializeMethod = type.GetMethod("Deserialize", BindingFlags.Static | BindingFlags.Public, MethodInfo? deserializeMethod = type.GetMethod("Deserialize", BindingFlags.Static | BindingFlags.Public,
null, null,
new []{ typeof(DeserializationObject), typeof(string), typeof(Type) }, null); new []{ typeof(DeserializationObject), typeof(string), typeof(Type) }, null);
@@ -79,7 +80,9 @@ public class DeserializationObject
} }
else else
{ {
_customDeserializers.Add(attribute.Type, deserializeMethod); DeserializeMethod method = deserializeMethod.GetDelegate<DeserializeMethod>();
_customDeserializers.Add(attribute.Type, method);
} }
} }
} }
@@ -264,7 +267,7 @@ public class DeserializationObject
return changed; return changed;
} }
private bool TryCustomDeserializer(string fieldName, Type fieldType, out object deserializedValue) private bool TryCustomDeserializer(string fieldName, Type fieldType, out object? deserializedValue)
{ {
deserializedValue = NoValueDeserialized; deserializedValue = NoValueDeserialized;
@@ -272,15 +275,15 @@ public class DeserializationObject
{ {
// Try to match the concrete type first (e.g. Foo -> Foo and Foo<Bar> -> List<Bar>) // Try to match the concrete type first (e.g. Foo -> Foo and Foo<Bar> -> List<Bar>)
// Note: Foo<Bar> wont match a serializer for Foo<> // Note: Foo<Bar> wont match a serializer for Foo<>
if (_customDeserializers.TryGetValue(fieldType, out MethodInfo deserializeMethod)) if (_customDeserializers.TryGetValue(fieldType, out DeserializeMethod deserializeMethod))
{ {
deserializedValue = deserializeMethod.Invoke(null, new object[] { this, fieldName, fieldType }); deserializedValue = deserializeMethod(this, fieldName, fieldType);
return true; return true;
} }
if (fieldType.IsGenericType && _customDeserializers.TryGetValue(fieldType.GetGenericTypeDefinition(), out deserializeMethod)) if (fieldType.IsGenericType && _customDeserializers.TryGetValue(fieldType.GetGenericTypeDefinition(), out deserializeMethod))
{ {
deserializedValue = deserializeMethod.Invoke(null, new object[] { this, fieldName, fieldType }); deserializedValue = deserializeMethod(this, fieldName, fieldType);
} return true; } return true;
} }
catch (Exception e) catch (Exception e)
+10 -6
View File
@@ -20,7 +20,9 @@ public class SerializedObject
private string _structScopeName; private string _structScopeName;
private static Dictionary<Type, MethodInfo> _customSerializers = new(); private delegate void SerializeMethod(SerializedObject container, string fieldName, object? fieldValue, Type fieldType);
private static Dictionary<Type, SerializeMethod> _customSerializers = new();
public UUID Id => _id; public UUID Id => _id;
@@ -53,7 +55,7 @@ public class SerializedObject
{ {
if (type.TryGetCustomAttribute<CustomSerializerAttribute>(out var attribute)) if (type.TryGetCustomAttribute<CustomSerializerAttribute>(out var attribute))
{ {
MethodInfo serializeMethod = type.GetMethod("Serialize", BindingFlags.Static | BindingFlags.Public, MethodInfo? serializeMethod = type.GetMethod("Serialize", BindingFlags.Static | BindingFlags.Public,
null, null,
new[] { typeof(SerializedObject), typeof(string), typeof(object), typeof(Type) }, null); new[] { typeof(SerializedObject), typeof(string), typeof(object), typeof(Type) }, null);
@@ -63,7 +65,9 @@ public class SerializedObject
} }
else else
{ {
_customSerializers.Add(attribute.Type, serializeMethod); SerializeMethod method = serializeMethod.GetDelegate<SerializeMethod>();
_customSerializers.Add(attribute.Type, method);
} }
} }
} }
@@ -116,15 +120,15 @@ public class SerializedObject
{ {
// Try to match the concrete type first (e.g. Foo -> Foo and Foo<Bar> -> List<Bar>) // Try to match the concrete type first (e.g. Foo -> Foo and Foo<Bar> -> List<Bar>)
// Note: Foo<Bar> wont match a serializer for Foo<> // Note: Foo<Bar> wont match a serializer for Foo<>
if (_customSerializers.TryGetValue(fieldType, out MethodInfo serializeMethod)) if (_customSerializers.TryGetValue(fieldType, out SerializeMethod serializeMethod))
{ {
serializeMethod.Invoke(null, new[] { this, fieldName, fieldValue, fieldType }); serializeMethod(this, fieldName, fieldValue, fieldType);
return true; return true;
} }
if (fieldType.IsGenericType && _customSerializers.TryGetValue(fieldType.GetGenericTypeDefinition(), out serializeMethod)) if (fieldType.IsGenericType && _customSerializers.TryGetValue(fieldType.GetGenericTypeDefinition(), out serializeMethod))
{ {
serializeMethod.Invoke(null, new[] { this, fieldName, fieldValue, fieldType }); serializeMethod(this, fieldName, fieldValue, fieldType);
return true; return true;
} }
} }