C # с помощью отражения для создания структуры - PullRequest
13 голосов
/ 17 июня 2009

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

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

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

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

SomeStruct.TypeInitializer.Invoke(null)

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

Ответы [ 3 ]

17 голосов
/ 17 июня 2009

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

Если структуры не являются неизменными, то убегайте от них как можно быстрее, если можете ... но если у вас абсолютно есть , чтобы сделать это, тогда используйте 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);
    }
}
4 голосов
/ 05 июля 2011

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

FormatterServices.GetUninitializedObject(Type type);

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

0 голосов
/ 17 июня 2009

Просто добавьте - с неизменными структурами, вам, вероятно, придется выполнить сопоставление параметров с конструктором. К сожалению, это сложно, когда может быть несколько конструкций, особенно потому, что некоторые типы имеют отдельный статический метод «Create» вместо открытого конструктора. Но при условии, что вы сделали сопоставление, вы все равно можете использовать 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...