简体   繁体   中英

Need generic solution in reflection: create instance which may or may not have parameterless constructors

I am working on a project where I am sending commands to a device. Each command object has a one-to-one relationship to a specific command supported by the device (which are defined by the API of a supporting dll).

I have XML files which define the commands: name the method name, and parameters (by name, type, and default value).

My application reads the XML and creates a command object. I have a parameter dictionary which has parameter names associated to values (by type).

When populating the parameter dictionary I started out creating a type, then an instance, then updating the dictionary.

{
    theType = Type.GetType(paramType);
    var newInstance = Activator.CreateInstance(theType, true);
    parameterValueMap.Add(paramName, newInstance);
}

This works fine for ints, etc, but doesn't work for string types, as they don't have a parameterless constructor.

As there may be other types with parameterless constructors, I need to keep the solution generic (not handle strings specifically).

Can somebody help me with the solution? Is there a way to create an instance of a type with parameters without having an actual instance of the parameter (and instance of string in my specific case)?

Thanks -m

Since you mention generics, a default(T) may be useful, but that will be null for classes / interfaces. A handy evil trick is:

T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));

However! This is pure evil, usually reserved for deserialization / materialization libraries. This is not intended for ad-hoc usage.

Perhaps the best approach is to let the caller tell you how, for example add an optional factory parameter:

 void YourMethod<T>(..., Func<T> constructor = null)

Then if they want the caller can supply a technique to create instances. So you have:

T obj = constructor == null ? (T)Activator.CreateInstance(typeof(T))
                            : constructor();

In any event: strings are a huge problem. You will undoubtably need to special-case strings.

I use a Activator.CreateInstance(type, ParseKeys(ID).ToArray()) in my code, where ID is a || separated list of key/value pairs, and ParseKeys() is

    private static IEnumerable<object> ParseKeys(string keyString)
    {
        IEnumerable<string> keyStrings = keyString.Split(new string[] { "||" }, StringSplitOptions.RemoveEmptyEntries); // Use this construction to enable splitting on multiple characters
        List<object> keys = new List<object>();
        foreach (var key in keyStrings)
        {
            var type = Type.GetType(key.Split("|")[0]);
            var value = key.Split("|")[1];
            var data = Convert.ChangeType(value, type);
            keys.Add(data);
        }
        return keys;
    }

Obviously, you need to be able to represent your parameters as a string in some form, and I haven't tested it for parameterless constructors, but it works just fine for both ints and strings.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM