I'm making a RTS game. Every Unit in RTS game can do some actions, such as Patrol, Attack or Build. In unity, you can easily manually fill-in string
and integer
arrays for C# scripts.
Because of this, I decided that it would be easiest to have a string[] str_actions
array for ever unit and when unit is first initialised, convert this array to Action[] actions
.
I can probably do this:
string className = "Attack"
Assembly assembly = Assembly.Load("Actions");
Type t = assembly.GetType("Actions."+className);
Action action = (Action)Activator.CreateInstance(t);
But this doesn't handle two problems:
- Action doesn't have constructor that takes 0 arguments
- The possibility that
className
refers to class that is not child of Action
How do I handle them?
To answer the question as posted:
Thats ok! Using this overload of Activator.CreateInstance: MSDN you can pass an object[] in, and it will find the constructor that best fits. Having a default constructor is a good idea though, especially if you are going to utilize serialization.
You can't "handle" it in the sense that you can avoid it from happening. However, your code as written will throw an InvalidCastException
if the cast fails. To avoid that, use the as
operator:
Action action = Activator.CreateInstance(t) as Action;
Now action
will just hold null
if the cast is invalid, instead of throwing.
Now for the caveat: Activator.CreateInstance
is very rarely the right choice in C#. In general, you want to use direct instantiation or deserialization. Granted, deserialization utilizes reflection; but all the messy details are abstracted away.
So I've figured it out. I'm making it a static method Action.fromString
. The thing I was lacking was the Type.GetConstructor
method which returns the ConstructorInfo
object.
public static Action fromString(string className, string defName, WorldObject actor)
{
//Get the Assembly (namespace)
Assembly assembly = Assembly.Load("Actions");
//Get the exact class Type
Type t = assembly.GetType("Actions." + className);
//Get the info about constructor (using array literal)
// - for every accepted parameter enter typeof(parameterType)
ConstructorInfo constructor = t.GetConstructor(new Type[] { typeof(string), typeof(WorldObject) });
//Initialise the Type instance
System.Object action = constructor.Invoke(new System.Object[] { defName, actor });
//If it's child of the main class
if (action is Action)
return (Action)action;
//Error otherwise
else
{
Debug.LogError("'" + className + "' is not child of Action!");
return null;
}
}