Goal:
I am trying to implement an Entity Component System and I'm stuck with my component implementation. I intend to have a generic component type which contains a static Bit for each component there is, e.g. components of type Position have Bit=0, components of type Sprite have Bit=1, etc. A component should only consist of the values it holds therefore I started with structs. Following is my attempt on this (not working code):
struct Position : Component<Position> //this does not work in c#
{
public int x, y;
}
internal struct ComponentBit
{
public static int bitCounter = 0;
};
public struct Component<T>
{
public static readonly int bit;
static Component()
{
bit = ComponentBit.bitCounter++;
}
public int GetBit()
{
return bit;
}
}
After that I found out that structs cannot be inherited from so I tried to change the structs to classes which worked.
Question:
Is there any way to implement these features with structs as it would be possible in C++ (templates)? I'd like to keep them as value types instead of reference types for implementation/performance reasons later on.
Edit1:
The intended usage I'd like to have is:
Position pos = new Position(x, y);
int bit1 = pos.GetBit(); // or
int bit2 = Position.bit; // both should be possible
My suggestion is to not try to use inheritance.
Any struct is a possible valid component, for example:
struct Position
{
public int X, Y;
public Position(int x, int y)
{
X = x;
Y = y;
}
}
Now, you can still have Component<T>
to work as a type to int dictionary (Dictionary<Type, int>
). Although, I'm going with least changes to make this work:
internal static class ComponentBit
{
public static int bitCounter = 0;
};
public static class Component<T>
{
public static readonly int bit; // This is static, it has a value per type T
static Component()
{
bit = ComponentBit.bitCounter++;
}
public static int GetBit()
{
// This method only accesses static members, it can be static
// Thus, no instance of T is needed
return bit;
}
}
And you would use it like this:
Position pos = new Position(x, y);
int bit1 = Component<Position>.GetBit();
int bit2 = Component<Position>.bit;
No idea why you want two ways of doing this, yet there you go.
Alright, now I will take the freedom to do bigger changes... with that in mind, if you want type inference you can do this:
public static class Component
{
internal static int bitCounter = 0;
public static int GetBit<T>()
where T : struct // We only accept structs here
{
return Component<T>.bit;
}
public static int GetBit<T>(this ref T value)
where T : struct // We only accept structs here
{
// This is an extension method.
// It will appear as a method on any valid T (which is all structs)
// The type T will be infered from the instance.
// Passing the struct as a reference to avoid a copy
_ = value; // discard value
return GetBit<T>();
}
};
internal static class Component<T>
where T : struct // We only accept structs here
{
internal static readonly int bit;
static Component()
{
bit = Component.bitCounter++;
}
}
Usage:
var pos = new Position(x, y);
var bit1 = Component.GetBit<Position>();
var bit2 = pos.GetBit();
Yes, the above code compiles and runs. See it in SharpLab.
Note: I would consider nesting Component<T>
in Component
.