C# using reflection to create a struct

前端 未结 3 1609
时光取名叫无心
时光取名叫无心 2020-12-20 11:57

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 a

相关标签:
3条回答
  • 2020-12-20 12:22

    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;
    }
    
    0 讨论(0)
  • 2020-12-20 12:38

    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);
        }
    }
    
    0 讨论(0)
  • 2020-12-20 12:39

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

    FormatterServices.GetUninitializedObject(Type type);
    

    This does the trick with blank structs.

    0 讨论(0)
提交回复
热议问题