简体   繁体   中英

C# using reflection to create a struct

I am currently writing some code to save general objects to XML using reflection in c#.

The problem is when reading the XML back in some of the objects are structs and I can't work out how to initialise the struct. For a class I can use

ConstructorInfo constructor = SomeClass.GetConstructor(Type.EmptyTypes);

however, for a struct, there is no constructor which takes no parameters so the above code sets constructor to null. I also tried

SomeStruct.TypeInitializer.Invoke(null)

but this throws a memberaccessexception. Google gives no promising hits. Any help would be appreciated.

If the values are structs, they're likely to be immutable - so you don't want to call a parameterless constructor, but the one which takes the appropriate values as constructor arguments.

If the structs aren't immutable, then run away from them as fast as possible, if you can... but if you absolutely have to do this, then use Activator.CreateInstance(SomeClass) . You'll have to be very careful when you use reflection to set properties or fields on the value type though - without that care, you'll end up creating a copy, changing the value on that copy, and then throwing it away. I suspect that if you work with a boxed version throughout, you'll be okay:

using System;

// Mutable structs - just say no...
public struct Foo
{
    public string Text { get; set; }
}

public class Test
{
    static void Main()
    {
        Type type = typeof(Foo);

        object value = Activator.CreateInstance(type);
        var property = type.GetProperty("Text");
        property.SetValue(value, "hello", null);

        Foo foo = (Foo) value;
        Console.WriteLine(foo.Text);
    }
}

CreateInstance will not help you with structs with no explicitly defined constructors.

FormatterServices.GetUninitializedObject(Type type);

This does the trick with blank structs.

Just to add - with immutable structs, you are likely to have to do parameter matching to the constructor. Unfortunately this is tricky when there can be multiple constructs, and especially since some types have a separate static "Create" method instead of a public constructor. But assuming you've done the matching, you can still use Activator.CreateInstance :

    Type type = typeof(Padding); // just an example
    object[] args = new object[] {1,2,3,4};
    object obj = Activator.CreateInstance(type, args);

However - the code to pick a constructor (the above has 3...) isn't easy. You could say "pick the most complex" and then attempt to match parameter names to property names (case insensitive)...

A naïve example:

static void Main() {
    Dictionary<string, object> propertyBag =
        new Dictionary<string, object>();
    // these are the values from your xml
    propertyBag["Left"] = 1;
    propertyBag["Top"] = 2;
    propertyBag["Right"] = 3;
    propertyBag["Bottom"] = 4;
    // the type to create
    Type type = typeof(Padding);

    object obj = CreateObject(type, propertyBag);

}
static object CreateObject(Type type, IDictionary<string,object> propertyBag)
{
    ConstructorInfo[] ctors = type.GetConstructors();
    // clone the property bag and make it case insensitive
    propertyBag = new Dictionary<string, object>(
        propertyBag, StringComparer.OrdinalIgnoreCase);
    ConstructorInfo bestCtor = null;
    ParameterInfo[] bestParams = null;
    for (int i = 0; i < ctors.Length; i++)
    {
        ParameterInfo[] ctorParams = ctors[i].GetParameters();
        if (bestCtor == null || ctorParams.Length > bestParams.Length)
        {
            bestCtor = ctors[i];
            bestParams = ctorParams;
        }
    }
    if (bestCtor == null) throw new InvalidOperationException(
         "Cannot create - no constructor");
    object[] args = new object[bestParams.Length];
    for (int i = 0; i < bestParams.Length; i++)
    {
        args[i] = propertyBag[bestParams[i].Name];
        propertyBag.Remove(bestParams[i].Name);
    }
    object obj = bestCtor.Invoke(args);
    // TODO: if we wanted, we could apply any unused keys in propertyBag
    // at this point via properties
    return obj;
}

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