С# с использованием отражения для создания структуры

В настоящее время я пишу код для сохранения общих объектов в XML, используя отражение в С#.

Проблема в том, что чтение XML в некоторых объектах - это структуры, и я не могу понять, как инициализировать структуру. Для класса я могу использовать

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

однако для структуры нет конструктора, который не принимает никаких параметров, поэтому приведенный выше код устанавливает конструктор в null. Я также пробовал

SomeStruct.TypeInitializer.Invoke(null)

но это вызывает исключение memberaccess. Google не дает многообещающих хитов. Любая помощь будет оценена.

Ответ 1

Если значения представляют собой структуры, они, вероятно, будут неизменными, поэтому вы не хотите называть конструктор без параметров, но тот, который принимает соответствующие значения в качестве аргументов конструктора.

Если структуры не являются неизменяемыми, тогда убегайте от них как можно быстрее, если можете... но если вам это абсолютно необходимо, используйте Activator.CreateInstance(SomeClass). Вы должны быть очень осторожны, когда используете рефлексию для установки свойств или полей в типе значений, но без этой помощи вы в конечном итоге создадите копию, измените значение на этой копии и затем выбросите ее. Я подозреваю, что если вы будете работать с коробкой версии, вы будете в порядке:

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

Ответ 2

CreateInstance не поможет вам с конструкциями без явно определенных конструкторов.

FormatterServices.GetUninitializedObject(Type type);

Это трюк с пустыми структурами.

Ответ 3

Чтобы добавить - с неизменяемыми структурами, вам, вероятно, придется выполнять сопоставление параметров с конструктором. К сожалению, это сложно, когда может быть несколько конструкций, и особенно потому, что некоторые типы имеют отдельный статический метод "Создать" вместо публичного конструктора. Но при условии вы выполнили сопоставление, вы все равно можете использовать Activator.CreateInstance:

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

Однако - код для выбора конструктора (выше имеет 3...) непросто. Вы могли бы сказать "выбрать наиболее сложный", а затем попытаться совместить имена параметров с именами свойств (нечувствительными к регистру)...

Наивный пример:

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