Reputation: 117
This is a 2 part question.
Firstly
I have a class called ComponentList that looks like this:
public class ComponentList<T> : List<T> where T : Component
{
}
Now I want to create an empty list of typeless ComponentLists:
List<ComponentList<>> MasterList = new List<ComponentList<>>();
I get an error because the ComponentList wants a generic type specified even though I'm not trying to initialize any ComponentLists yet (just the MasterList that contains them). How would I declare the MasterList of ComponentLists without initializing any ComponentLists (as I plan to initialize them during runtime with types only known at runtime)? After all, the MasterList needs to contain ComponentLists of different generic types, not just one.
Secondly
I know this one has been asked before, but I can't seem to wrap my head around the concepts of any of the proposed solutions.
As you know, I have a List<>
called MasterList which is a list of ComponentLists (a custom class). A ComponentList is a generic class of an undefined type (that is constrained to being a subtype of Component).
In the following example, I'm trying to check if a ComponentList generic type (referenced from MasterList) is the same as this class (the class the code's being called from).
if (MasterList[i].GetType() == typeof(ComponentList<this.GetType()>))
{
}
Problem is, the code is meant to be automatically called from unknown child classes, not from this parent class. So the ComponentList generic type needs to be compared with the type of the child class, not this base class. Hence the "this.GetType" inplace of what would be the hardcoded name of the actual base class.
The this.GetType()
type passed in the ComponentList<> returns a "type expected" error apparently because GetType()
returns compile time type, not runtime type (which is what I need).
So how could I get the runtime type? And if I can't, what would be the best alternative to accomplishing what I'm trying to do? (I've heard a bit about something called reflections that might help me but I really don't understand).
Upvotes: 3
Views: 4747
Reputation: 14668
I've run into this issue as well when I was trying to set up an entity-component system for a game written in C#. There's really isn't a way to store components as their actual types, you have to store them all as Component
s and cast them.
The way I have it set up is as a Dictionary<Type, List<Component>>
as a private member of a ComponentManager
class. The method that adds components is generic and checks if it's Type is contained in the Dictionary, so getting an IEnumerable<SpecificComponent>
is as simple as:
public IEnumerable<T> EnumerateComponents<T>()
where T : Component
{
foreach (Component c in components[typeof(T)])
yield return (T)c;
}
(You'll also want to check that the dictionary contains typeof(T)
, that bit is built-in with a custom collection of mine that inherits from Dictionary to avoid exceptions in cases like this.)
Type safety is "guaranteed" as long as the dictionary is never modified outside of a generic method (and definitely not directly accessible from the outside). Not ideal, but it's fast enough where it will never be your bottleneck.
EDIT
Something to explore might be C# 4's dynamic
keyword. I haven't looked into it much, but storing the components as a List<dynamic>
might work better (or it may introduce way too much overhead), just something to think about.
Upvotes: 0
Reputation: 2668
You cannot determine generic types at runtime. C# (all .Net, actually) is designed that way on purpose. The compiler treats a generic type virtually same way as an explicitly defined type.
Consider the following:
class MyGenericClass<T>
{
public static T MyProperty { get; set; }
}
class MyIntClass
{
public static int MyProperty { get; set; }
}
class UseThem
{
public void test()
{
// both are JIT'ed to be exactly the same machine code
var foo = MyGenericClass<int>.MyProperty;
var bar = MyOtherClass.MyProperty;
}
}
Thus, you must provide a type for the generic class so that JIT knows what to compile.
Possible Alternative If all of the possible types which could possible end up being the generic type are similar (inherit from the same base class or implement a similar interface), then you can instantiate the generic class with an interface or base class as the generic type:
List<ComponentList<ComponentBase>> MasterList = new List<ComponentList<ComponentBase>>();
// -OR-
List<ComponentList<IMyComponent>> MasterList = new List<ComponentList<IMyComponent>>();
I have a hunch that you should be able to define a common interface or base class with a little creative refactoring. :)
Upvotes: 2
Reputation: 127
I don't think what you are asking is possible, however maybe this is what you need.
public class ComponentA : Component { }
public class ComponentB : Component { }
public class Component { }
public class ComponentList<T> : List<T> where T : Component
{
}
class Program
{
static void Main(string[] args)
{
ComponentList<Component> MasterList = new ComponentList<Component>();
MasterList.Add(new ComponentA());
MasterList.Add(new ComponentB());
for (int i = 0; i < MasterList.Count; i++)
{
if (MasterList[i] is ComponentA)
{
}
}
}
}
Upvotes: -1