refactor: replace boxed component addition with typed delegate

This commit is contained in:
hypercross 2026-07-18 23:44:10 +08:00
parent 96ba037432
commit 1da0ca30d3
4 changed files with 9 additions and 33 deletions

View File

@ -211,7 +211,8 @@ public class ComponentDiscoveryGenerator : IIncrementalGenerator
sb.AppendLine($" typeName: \"{aqn}\",");
sb.AppendLine($" type: typeof({fqn}),");
sb.AppendLine($" serialize: obj => MessagePackSerializer.Serialize(({fqn})obj),");
sb.AppendLine($" deserialize: data => MessagePackSerializer.Deserialize<{fqn}>(data)");
sb.AppendLine($" deserializeAndAdd: (world, entity, data) =>");
sb.AppendLine($" world.AddComponent(entity, MessagePackSerializer.Deserialize<{fqn}>(data))");
sb.Append(" )");
}

View File

@ -23,19 +23,20 @@ public sealed class ComponentDescriptor
public Func<object, byte[]> Serialize { get; }
/// <summary>
/// Deserializes a MessagePack byte array into a boxed component instance.
/// Deserializes a MessagePack byte array and adds the component to the entity
/// via the typed <see cref="World.AddComponent{T}"/> method. No reflection.
/// </summary>
public Func<byte[], object> Deserialize { get; }
public Action<World, Entity, byte[]> DeserializeAndAdd { get; }
public ComponentDescriptor(
string typeName,
Type type,
Func<object, byte[]> serialize,
Func<byte[], object> deserialize)
Action<World, Entity, byte[]> deserializeAndAdd)
{
TypeName = typeName;
Type = type;
Serialize = serialize;
Deserialize = deserialize;
DeserializeAndAdd = deserializeAndAdd;
}
}

View File

@ -1,5 +1,4 @@
using System.Diagnostics;
using System.Reflection;
using R3;
namespace OECS;
@ -665,27 +664,6 @@ public class World : IDisposable
/// </summary>
internal ComponentStore Components => _components;
/// <summary>
/// Adds a boxed component to an entity by its runtime type.
/// Used by <see cref="WorldSerializer"/> during deserialization to
/// invoke the typed <see cref="AddComponent{T}"/> without reflection.
/// </summary>
internal void AddComponentBoxed(Type componentType, Entity entity, object component)
{
// Use a cached delegate per type to avoid reflection on every call.
if (!_addComponentDelegates.TryGetValue(componentType, out var del))
{
var method = typeof(World).GetMethod(nameof(AddComponent))!
.MakeGenericMethod(componentType);
del = (Action<World, Entity, object>)Delegate.CreateDelegate(
typeof(Action<World, Entity, object>), method);
_addComponentDelegates[componentType] = del;
}
del(this, entity, component);
}
private readonly Dictionary<Type, Action<World, Entity, object>> _addComponentDelegates = new();
// ── Helpers ───────────────────────────────────────────────────────
private void ThrowIfNotAlive(Entity entity)

View File

@ -93,12 +93,8 @@ public static class WorldSerializer
$"Ensure the component type is used with the World " +
$"API so the source generator can discover it.");
var component = desc.Deserialize(ce.Data)
?? throw new InvalidOperationException(
$"Failed to deserialize component of type '{ce.TypeName}'.");
// Use the generated typed delegate to add the component.
world.AddComponentBoxed(desc.Type, entity, component);
// Deserialize and add in one typed operation — no reflection.
desc.DeserializeAndAdd(world, entity, ce.Data);
}
}
}