refactor: add struct constraint to Relationship

Add `where TSelf : struct where TTarget : struct` constraints to
the `Relationship` struct to ensure type safety and consistency with
the ECS architecture. Also remove unused `IterateThree` method in
`QueryExecutor`.
This commit is contained in:
hypercross 2026-07-18 22:05:29 +08:00
parent 58477aec50
commit b6e64d2871
3 changed files with 7 additions and 47 deletions

View File

@ -179,48 +179,6 @@ internal static class QueryExecutor
}
}
private static void IterateThree<TDriver, TA, TB>(
SparseSet<TDriver> driveSet,
SparseSet<TA> setA,
SparseSet<TB> setB,
ComponentStore store,
IReadOnlySet<Type> withoutTypes,
ForEachAction<TA, TDriver, TB> actionWhenDriverIsT2,
ForEachAction<TDriver, TA, TB> actionWhenDriverIsT1,
ForEachAction<TA, TB, TDriver> actionWhenDriverIsT3)
where TDriver : struct where TA : struct where TB : struct
{
var dense = driveSet.Dense;
var denseEntities = driveSet.DenseEntities;
var count = driveSet.Count;
for (int i = 0; i < count; i++)
{
var entity = denseEntities[i];
if (entity.Id == SingletonId) continue;
if (!setA.Contains(entity)) continue;
if (!setB.Contains(entity)) continue;
if (!PassesWithoutFilter(store, entity, withoutTypes))
continue;
if (actionWhenDriverIsT2 != null!)
{
// Driver is T2, so order is: T1=setA, T2=driveSet, T3=setB
actionWhenDriverIsT2(entity, ref setA.Get(entity), ref dense[i], ref setB.Get(entity));
}
else if (actionWhenDriverIsT3 != null!)
{
// Driver is T3, so order is: T1=setA, T2=setB, T3=driveSet
actionWhenDriverIsT3(entity, ref setA.Get(entity), ref setB.Get(entity), ref dense[i]);
}
else
{
// Driver is T1, so order is: T1=driveSet, T2=setA, T3=setB
actionWhenDriverIsT1(entity, ref dense[i], ref setA.Get(entity), ref setB.Get(entity));
}
}
}
public static void ForEach<T1, T2, T3, T4>(
ComponentStore store,
QueryDescriptor query,

View File

@ -25,6 +25,7 @@ namespace OECS;
/// </summary>
[MessagePackObject]
public struct Relationship<TSelf, TTarget> : IRelationship
where TSelf : struct where TTarget : struct
{
/// <summary>
/// The entity that owns this relationship component.

View File

@ -9,6 +9,7 @@ public struct ChildOf { }
public struct ParentOf { }
public struct OwnedBy { }
public struct MemberOf { }
public struct Thing { }
// ── Tests ────────────────────────────────────────────────────────────
@ -153,20 +154,20 @@ public class RelationshipTests
var owner = world.CreateEntity();
var group = world.CreateEntity();
world.AddComponent(entity, new Relationship<OwnedBy, object>
world.AddComponent(entity, new Relationship<OwnedBy, Thing>
{
Source = entity, Target = owner
});
world.AddComponent(entity, new Relationship<MemberOf, object>
world.AddComponent(entity, new Relationship<MemberOf, Thing>
{
Source = entity, Target = group
});
world.GetSources<Relationship<OwnedBy, object>>(owner).Should().BeEquivalentTo([entity]);
world.GetSources<Relationship<MemberOf, object>>(group).Should().BeEquivalentTo([entity]);
world.GetSources<Relationship<OwnedBy, Thing>>(owner).Should().BeEquivalentTo([entity]);
world.GetSources<Relationship<MemberOf, Thing>>(group).Should().BeEquivalentTo([entity]);
// Cross-check: OwnedBy sources should not appear in MemberOf lookups.
world.GetSources<Relationship<MemberOf, object>>(owner).Should().BeEmpty();
world.GetSources<Relationship<MemberOf, Thing>>(owner).Should().BeEmpty();
}
[Fact]