I had two interesting problems to solve during the development of Svelto.ECS, which in short are:

  • Set an implemented property of a struct, but only when the struct implements a specific interface, from a generic method and without casting the struct to the interface
  • Assign an object to a field of a struct, using reflection, without casting the struct to an object

Cast a struct to an interface when generic parameter constraints aren’t viable

In Svelto.ECS all the entity components are structs. They can either implement IEntityComponent or IEntityViewComponent and, optionally, INeedEGID.

When INeedEGID is used, the user is asked to implement a property, in the entity component, that holds the Entity ID. This Entity ID is updated automatically when it changes, this means that at run time I need to change it. Since I use generic methods to access this data, I cannot use the constraint INeedEGID for the entity component type, as it may or may not have it.

The way I solve this is to create a field setter delegate that takes as input the entity component. The delegate generating code checks if T implements or not INeedEGID, if it does, it returns a delegate that works for that type, casting the parameter T to INeedEGID and assigning the value to it. However, in c#, casting a struct to an interface means boxing and since my code is allocations 0, I had to find a solution for it.

My solution for the Svelto.ECS 2.x cycle was using dynamic generated code, like this:

I would then use the generated delegate like in this hypothetical example:

As dynamically generated code cannot be used with IL2CPP, I used to fall back to a simple cast, which in native code doesn’t box (as far as my experiments showed to me). However I lately realised that the Unity implementation of the .Net Standard 2.0 interfaces do not implement ANY code that generates IL dynamically. In order to make Svelto.ECS (Unity) .net standard 2.0 compatible, I decided to get rid of dynamically generated code.

I made several experiments, but eventually StackOverflow came to the rescue and the solution was fascinating (because I would have never thought about it):

In fact, I have never thought that I could create, with reflection, a delegate to a generic method with constraint! When the delegate returned is called, U will be T, but for the compiler, it also implements INeedEGID, hence no need for boxing!


Set the value of a struct field with reflection without boxing

In my library I have another similar problem, but this time a bit more complicated. I have to be able to fill fields of a struct, using reflection, but without casting the struct to an object (which reflection wants me to do!). I initially solved this problem with dynamically generated code too:

This one was a bit trickier, because this time I don’t have any interface to rely upon. However the IL2CPP version shows again how simple the code I need is. It’s just that while in IL2CPP it doesn’t cause any problem, in c# that simple cost would cost an allocation.

In Svelto.ECS I achieve OOP abstraction through the IEntityViewComponent. It’s a struct that holds interfaces (one or more) that can hold ONLY value types returning properties, like this one:

When an entity is built, a list of implementors is passed to Svelto. The implementors must satisfy all the interfaces used inside the IEntityViewComponents.
so what Svelto must do is to match all the interfaces with all the implementors and in doing so it must not occur to any boxing either.

The way I would use the generated delegate like:

To solve this problem, at the moment, I rely on reflection, looking for the fields and using the SetValue FieldInfo method. However SetValue would cast anything passed to an object, so the IEntityViewComponent struct would be boxed. To avoid this, I resorted again to dynamic generated code.

To get rid of the previous solution, I had to take advantage or some internals that really I would like to not use. Let’s see how I solved it (until I find a better solution at least):

My reasoning here is that, if I find the offset of the field that represents the reference to the object, I could assign the object directly using unsafe code. I am stepping into a very dangerous territory here, because while the structs work in a similar way to what you would expect in c++, c# objects are another beast. They come in fact with some extra bytes that are used as header for GC, referencing, reflection and so on. Anyway I decided to bet on the idea. My first problem to solve was how to find the offset of the field I am looking for. This is the part I don’t like, as it heavily relies on the internal implementation and could break with future releases of .Net. The solution would be safe with Unity, as Unity actually provides officially a method to find the offset of a field in a struct. This is the code of GetFieldOffset:

I had to use a define, because UnsafeUtility is available only in Unity, while the other approach wouldn’t work with Mono. By the way, in case you wonder what GetFieldOffset(RuntimeFieldHandle) does, well you can check the stack overflow answer where I stole it from.

My bet eventually paid out, because the code:

worked at first try! (I never trust code working at first try).

Anyway. I don’t like this approach very much, so if you know an alternative solution, please let me know!

Leave a Reply

avatar
  Subscribe  
Notify of