С# с использованием отражения для создания структуры
В настоящее время я пишу код для сохранения общих объектов в 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;
}