Make a class instance from string and call a const

2019-07-24 02:23发布

问题:

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:

  1. Action doesn't have constructor that takes 0 arguments
  2. The possibility that className refers to class that is not child of Action

How do I handle them?

回答1:

To answer the question as posted:

  1. 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.

  2. 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.



回答2:

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;
        }
    }


标签: c# reflection