From 2c62df94d3e29b7b446b8c4ac0db87474a51b0d4 Mon Sep 17 00:00:00 2001
From: Tesmi <88522894+Tesmi-Develop@users.noreply.github.com>
Date: Sat, 9 May 2026 20:58:56 +1000
Subject: [PATCH 1/4] feat: added world dynamic methods
---
src/Hypercube.Ecs/World.Dynamic.cs | 143 +++++++++++++++++++++++++++++
1 file changed, 143 insertions(+)
create mode 100644 src/Hypercube.Ecs/World.Dynamic.cs
diff --git a/src/Hypercube.Ecs/World.Dynamic.cs b/src/Hypercube.Ecs/World.Dynamic.cs
new file mode 100644
index 0000000..bacca80
--- /dev/null
+++ b/src/Hypercube.Ecs/World.Dynamic.cs
@@ -0,0 +1,143 @@
+using System.Reflection;
+using JetBrains.Annotations;
+
+namespace Hypercube.Ecs;
+
+///
+/// Provides a dynamic bridge for component manipulation using runtime information.
+///
+public partial class World
+{
+ ///
+ /// Caches fully constructed generic methods to avoid repeated calls.
+ /// Key: (Method Name, Component Type).
+ ///
+ private readonly Dictionary<(string Name, Type GenericType), MethodInfo> _methodCache = new();
+
+ ///
+ /// Caches open generic method definitions to avoid expensive lookups and LINQ filtering.
+ /// Key: (Method Name, Parameter Count).
+ ///
+ private readonly Dictionary<(string Name, int ParamCount), MethodInfo> _genericDefinitions = new();
+
+ ///
+ /// Dynamically adds a component of the specified to an entity.
+ /// Uses the default constructor for the component type.
+ ///
+ /// The target entity.
+ /// The runtime type of the component to add.
+ /// The newly created component instance.
+ public object Add(Entity entity, Type type)
+ {
+ var method = GetGenericMethod(nameof(Add), type, Type.EmptyTypes);
+ return method.Invoke(this, [entity])!;
+ }
+
+ ///
+ /// Dynamically adds a specific component instance to an entity.
+ ///
+ /// The target entity.
+ /// The component instance to add. If null, the operation is ignored.
+ public void Add(Entity entity, object? value)
+ {
+ if (value is null)
+ return;
+
+ var type = value.GetType();
+ // Finds the overload: Add(Entity entity, ref T component)
+ var method = GetGenericMethod(nameof(Add), type, [typeof(Entity), type.MakeByRefType()]);
+ method.Invoke(this, [entity, value]);
+ }
+
+ ///
+ /// Dynamically retrieves a component instance of the specified from an entity.
+ ///
+ /// The target entity.
+ /// The runtime type of the component to retrieve.
+ /// The component instance.
+ public object Get(Entity entity, Type type)
+ {
+ var method = GetGenericMethod(nameof(Get), type, [typeof(Entity)]);
+ return method.Invoke(this, [entity])!;
+ }
+
+ ///
+ /// Dynamically checks if an entity has a component of the specified .
+ ///
+ /// The target entity.
+ /// The runtime type of the component to check.
+ /// True if the entity possesses the component; otherwise, false.
+ public bool Has(Entity entity, Type type)
+ {
+ var method = GetGenericMethod(nameof(Has), type, [typeof(Entity)]);
+ return (bool)method.Invoke(this, [entity])!;
+ }
+
+ ///
+ /// Dynamically removes a component of the specified from an entity.
+ ///
+ /// The target entity.
+ /// The runtime type of the component to remove.
+ public void Remove(Entity entity, Type type)
+ {
+ var method = GetGenericMethod(nameof(Remove), type, [typeof(Entity)]);
+ method.Invoke(this, [entity]);
+ }
+
+ ///
+ /// Resolves and caches a generic method using a two-tier caching strategy.
+ ///
+ /// The name of the method to resolve.
+ /// The type argument for the generic method.
+ /// The types of the method parameters used to resolve overloads.
+ /// A constructed ready for invocation.
+ private MethodInfo GetGenericMethod(string name, Type genericType, Type[] parameterTypes)
+ {
+ var cacheKey = (name, genericType);
+ if (_methodCache.TryGetValue(cacheKey, out var cached))
+ return cached;
+
+ var definitionKey = (name, parameterTypes.Length);
+ if (!_genericDefinitions.TryGetValue(definitionKey, out var methodDefinition))
+ {
+ methodDefinition = GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
+ .First(m => m.Name == name &&
+ m.IsGenericMethod &&
+ m.GetGenericArguments().Length == 1 &&
+ MatchParameters(m.GetParameters(), parameterTypes));
+
+ _genericDefinitions[definitionKey] = methodDefinition;
+ }
+
+ var genericMethod = methodDefinition.MakeGenericMethod(genericType);
+ _methodCache[cacheKey] = genericMethod;
+
+ return genericMethod;
+ }
+
+ ///
+ /// Validates if a method's parameters match the expected types,
+ /// accounting for generic placeholders and reference types.
+ ///
+ /// The parameters from the .
+ /// The expected runtime types.
+ /// True if the signatures match; otherwise, false.
+ private bool MatchParameters(ParameterInfo[] parameters, Type[] parameterTypes)
+ {
+ if (parameters.Length != parameterTypes.Length)
+ return false;
+
+ for (var i = 0; i < parameters.Length; i++)
+ {
+ var paramType = parameters[i].ParameterType;
+
+ if (paramType.IsGenericParameter || (paramType.IsByRef && paramType.GetElementType()!.IsGenericParameter))
+ continue;
+
+ if (paramType != parameterTypes[i])
+ return false;
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
From 37c23f9b78627e70b1502932c784014b8ab0d460 Mon Sep 17 00:00:00 2001
From: Tesmi <88522894+Tesmi-Develop@users.noreply.github.com>
Date: Sun, 17 May 2026 16:39:59 +1000
Subject: [PATCH 2/4] feat: added IWorld.Dynamic
---
src/Hypercube.Ecs/IWorld.Dynamic.cs | 36 +++++++++++++++++++++++++++++
src/Hypercube.Ecs/IWorld.cs | 2 +-
2 files changed, 37 insertions(+), 1 deletion(-)
create mode 100644 src/Hypercube.Ecs/IWorld.Dynamic.cs
diff --git a/src/Hypercube.Ecs/IWorld.Dynamic.cs b/src/Hypercube.Ecs/IWorld.Dynamic.cs
new file mode 100644
index 0000000..67667ab
--- /dev/null
+++ b/src/Hypercube.Ecs/IWorld.Dynamic.cs
@@ -0,0 +1,36 @@
+namespace Hypercube.Ecs;
+
+public partial interface IWorld
+{
+ public object Add(Entity entity, Type type);
+
+ ///
+ /// Dynamically adds a specific component instance to an entity.
+ ///
+ /// The target entity.
+ /// The component instance to add. If null, the operation is ignored.
+ public void Add(Entity entity, object? value);
+
+ ///
+ /// Dynamically retrieves a component instance of the specified from an entity.
+ ///
+ /// The target entity.
+ /// The runtime type of the component to retrieve.
+ /// The component instance.
+ public object Get(Entity entity, Type type);
+
+ ///
+ /// Dynamically checks if an entity has a component of the specified .
+ ///
+ /// The target entity.
+ /// The runtime type of the component to check.
+ /// True if the entity possesses the component; otherwise, false.
+ public bool Has(Entity entity, Type type);
+
+ ///
+ /// Dynamically removes a component of the specified from an entity.
+ ///
+ /// The target entity.
+ /// The runtime type of the component to remove.
+ public void Remove(Entity entity, Type type);
+}
\ No newline at end of file
diff --git a/src/Hypercube.Ecs/IWorld.cs b/src/Hypercube.Ecs/IWorld.cs
index 664d910..f06959d 100644
--- a/src/Hypercube.Ecs/IWorld.cs
+++ b/src/Hypercube.Ecs/IWorld.cs
@@ -4,7 +4,7 @@
namespace Hypercube.Ecs;
-public interface IWorld
+public partial interface IWorld
{
IEventBus Events { get; }
From e05b429e5cbb137985bde01b68ea270292949575 Mon Sep 17 00:00:00 2001
From: Tesmi <88522894+Tesmi-Develop@users.noreply.github.com>
Date: Sun, 17 May 2026 16:43:30 +1000
Subject: [PATCH 3/4] ref: documentation
---
src/Hypercube.Ecs/World.Dynamic.cs | 35 +++++-------------------------
1 file changed, 5 insertions(+), 30 deletions(-)
diff --git a/src/Hypercube.Ecs/World.Dynamic.cs b/src/Hypercube.Ecs/World.Dynamic.cs
index bacca80..fcac25d 100644
--- a/src/Hypercube.Ecs/World.Dynamic.cs
+++ b/src/Hypercube.Ecs/World.Dynamic.cs
@@ -20,64 +20,39 @@ public partial class World
///
private readonly Dictionary<(string Name, int ParamCount), MethodInfo> _genericDefinitions = new();
- ///
- /// Dynamically adds a component of the specified to an entity.
- /// Uses the default constructor for the component type.
- ///
- /// The target entity.
- /// The runtime type of the component to add.
- /// The newly created component instance.
+ ///
public object Add(Entity entity, Type type)
{
var method = GetGenericMethod(nameof(Add), type, Type.EmptyTypes);
return method.Invoke(this, [entity])!;
}
- ///
- /// Dynamically adds a specific component instance to an entity.
- ///
- /// The target entity.
- /// The component instance to add. If null, the operation is ignored.
+ ///
public void Add(Entity entity, object? value)
{
if (value is null)
return;
var type = value.GetType();
- // Finds the overload: Add(Entity entity, ref T component)
var method = GetGenericMethod(nameof(Add), type, [typeof(Entity), type.MakeByRefType()]);
method.Invoke(this, [entity, value]);
}
- ///
- /// Dynamically retrieves a component instance of the specified from an entity.
- ///
- /// The target entity.
- /// The runtime type of the component to retrieve.
- /// The component instance.
+ ///
public object Get(Entity entity, Type type)
{
var method = GetGenericMethod(nameof(Get), type, [typeof(Entity)]);
return method.Invoke(this, [entity])!;
}
- ///
- /// Dynamically checks if an entity has a component of the specified .
- ///
- /// The target entity.
- /// The runtime type of the component to check.
- /// True if the entity possesses the component; otherwise, false.
+ ///
public bool Has(Entity entity, Type type)
{
var method = GetGenericMethod(nameof(Has), type, [typeof(Entity)]);
return (bool)method.Invoke(this, [entity])!;
}
- ///
- /// Dynamically removes a component of the specified from an entity.
- ///
- /// The target entity.
- /// The runtime type of the component to remove.
+ ///
public void Remove(Entity entity, Type type)
{
var method = GetGenericMethod(nameof(Remove), type, [typeof(Entity)]);
From e8cea72019eac7b7ca1bb81680dc31324cc27a2a Mon Sep 17 00:00:00 2001
From: Tornado Tech <54727692+TornadoTechnology@users.noreply.github.com>
Date: Sun, 17 May 2026 16:44:40 +1000
Subject: [PATCH 4/4] ref: remove world header
---
src/Hypercube.Ecs/World.Dynamic.cs | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/Hypercube.Ecs/World.Dynamic.cs b/src/Hypercube.Ecs/World.Dynamic.cs
index fcac25d..68df0a0 100644
--- a/src/Hypercube.Ecs/World.Dynamic.cs
+++ b/src/Hypercube.Ecs/World.Dynamic.cs
@@ -3,9 +3,6 @@
namespace Hypercube.Ecs;
-///
-/// Provides a dynamic bridge for component manipulation using runtime information.
-///
public partial class World
{
///
@@ -115,4 +112,4 @@ private bool MatchParameters(ParameterInfo[] parameters, Type[] parameterTypes)
return true;
}
-}
\ No newline at end of file
+}