【问题标题】:Reflection - Call constructor with parameters反射 - 使用参数调用构造函数
【发布时间】:2016-12-01 15:56:54
【问题描述】:

例如,我从加载的程序集中读取类型:

var someType = loadedAssemblies
            .Where(a => a != null && a.FullName.StartsWith("MY."))
            .SelectMany(a => a.GetTypes())
            .Distinct()
            .ToArray()[0];

如果 counstructor 有参数,我可以读取它们:

ParameterInfo[] parameters = classType.GetConstructors()[0].GetParameters();

我想用默认参数值调用构造函数,或者如果参数是枚举,则使用第一个枚举值。 如果只有一个参数并且是枚举,它的工作原理是这样的:

object curObject = Activator.CreateInstance(classType, new object[] { parameters[0].ParameterType.GetEnumValues().GetValue(0) });

当有更多参数时,我该怎么做? 我需要创建对象来读取属性:

var propertyInfo = someType.GetProperty("EntityType");
string entityType = propertyInfo.GetValue(curObject, null).ToString();

【问题讨论】:

    标签: c# reflection enums


    【解决方案1】:

    好吧,你可以创建自己的工厂,并编写一个方法,检查构造函数的类型并使用其默认值运行第一个参数化 ctor:

    public static class MyFactory
    {
        public static T MyCreateInstance<T>()
            where T : class
        {
            return (T) MyCreateInstance(typeof (T));
        }
    
        public static object MyCreateInstance(Type type)
        {
            var parametrizedCtor = type
                .GetConstructors()
                .FirstOrDefault(c => c.GetParameters().Length > 0);
    
            return parametrizedCtor != null
                ? parametrizedCtor.Invoke
                    (parametrizedCtor.GetParameters()
                        .Select(p =>
                            p.HasDefaultValue? p.DefaultValue :
                            p.ParameterType.IsValueType && Nullable.GetUnderlyingType(p.ParameterType) == null
                                ? Activator.CreateInstance(p.ParameterType)
                                : null
                        ).ToArray()
                    )
                : Activator.CreateInstance(type);
        }
    }
    

    然后用这个方法:

    var classType = loadedAssemblies
                .Where(a => a != null && a.FullName.StartsWith("MY."))
                .SelectMany(a => a.GetTypes())
                .Distinct()
                .ToArray()[0];
    
    var curObject = MyFactory.MyCreateInstance(classType);
    
    // This will return an array of values
    
    object[] values = classType
                     .GetFields()
                     .Select(f => f.GetValue(curObject))
                     .ToArray();
    

    附: 这是DotNet fiddle example

    更新:

    代码会根据您使用的场景进行更改。现在我们有两种方法, 一个返回对象,另一个可以将其转换为 T 类型。

    我也更新了 DotnetFiddle,请检查一下。

    【讨论】:

    • 嗨 Fabjan,感谢您提供此解决方案。有一个问题。我只有在运行时读取的类类型。我不知道编译时的对象(类)名称。您的代码: var curObject = MyFactory.MyCreateInstance(classType);不适用于对象关键字。
    • 我已经更新了我的答案以及 DotNetfiddle 上的代码示例
    • 谢谢你,它工作得很好。你知道constructor.Invoke vs Activator.CreateInstance的区别吗?
    【解决方案2】:

    您可以创建一个辅助方法来获取类型的默认值:

    private static object GetDefaultValue(Type type)
    {
        if (type.IsEnum) return type.GetEnumValues().GetValue(0);
        if (type.IsValueType) return Activator.CreateInstance(type);
        return null;
    }
    

    然后,您可以获取参数的默认值:

    var parameters = constructor.GetParameters()
                                .Select(p => GetDefaultValue(p.ParameterType))
                                .ToArray();
    

    并调用ConstructorInfo 来获取实例:

    var obj = constructor.Invoke(parameters);
    

    如果构造函数的参数有默认值并且你想使用它们,你可以这样做:

    var parameters = constructor
        .GetParameters()
        .Select(p => p.HasDefaultValue ? p.RawDefaultValue : GetDefaultValue(p.ParameterType))
        .ToArray();
    

    【讨论】:

    • Arturo,constructor.Invoke 和 Activator.CreateInstance 有什么区别?你能对 Activator.CreateInstance 做同样的事情吗?
    • @Simon: Constructor.Invoke 直接调用该构造函数。 Activator.CreateInstance 创建一个实例,查找与指定参数最匹配的构造函数。
    猜你喜欢
    • 1970-01-01
    • 2011-06-19
    • 1970-01-01
    • 2020-01-11
    • 2019-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多