C#使用反射创建结构体

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1005336/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 05:19:44  来源:igfitidea点击:

C# using reflection to create a struct

c#reflection

提问by

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

我目前正在编写一些代码来使用 c# 中的反射将一般对象保存到 XML。

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

问题是在某些对象中读取 XML 时是结构,我无法弄清楚如何初始化该结构。对于我可以使用的课程

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

但是,对于结构,没有不带参数的构造函数,因此上面的代码将构造函数设置为 null。我也试过

SomeStruct.TypeInitializer.Invoke(null)

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

但这会引发成员访问异常。谷歌没有给出有希望的点击。任何帮助,将不胜感激。

回答by Jon Skeet

If the values are structs, they're likelyto 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'timmutable, then run away from them as fast as possible, if you can... but if you absolutely haveto 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 suspectthat if you work with a boxed version throughout, you'll be okay:

如果结构不是一成不变的,那么尽可能快地远离它们,如果可以的话……但如果你绝对必须这样做,那么使用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);
    }
}

回答by Marc Gravell

Just to add - with immutablestructs, 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 assumingyou've done the matching, you can still use Activator.CreateInstance:

只是添加 - 使用不可变结构,您可能必须对构造函数进行参数匹配。不幸的是,当存在多个构造时,这很棘手,尤其是因为某些类型具有单独的静态“Create”方法而不是公共构造函数。但是假设您已经完成匹配,您仍然可以使用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)...

但是 - 选择构造函数的代码(上面有 3 个......)并不容易。您可以说“选择最复杂的”,然后尝试将参数名称与属性名称匹配(不区分大小写)...

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

回答by boskicthebrain

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

CreateInstance 不会帮助您处理没有明确定义的构造函数的结构。

FormatterServices.GetUninitializedObject(Type type);

This does the trick with blank structs.

这对空白结构有效。