简体   繁体   English

Activator.CreateInstance以类为参数调用构造函数

[英]Activator.CreateInstance calling constructor with class as parameter

Hi I'm trying to do the following dynamically I'm using my own CreateInstance method but this has been tested with Activator.CreateInstance 嗨,我正在尝试动态地执行以下操作,我正在使用自己的CreateInstance方法,但是已经通过Activator.CreateInstance测试过

IPEndPoint newObject = new IPEndPoint(IPAddress.Any, 80);

when I try to use activator I get error, cannot convert System.RuntimeType to IPAddress 当我尝试使用激活器时出现错误,无法将System.RuntimeType转换为IPAddress

    public static object CreateInstance(Type context, object[] Params)
    {
        List<Type> argTypes = new List<Type>();

        foreach (object Param in Params)
            argTypes.Add(GetType(Param));
        ConstructorInfo[] Types = context.GetConstructors();
        foreach (ConstructorInfo node in Types)
        {
            ParameterInfo[] Args = node.GetParameters();
            if (Params.Length == Args.Length)
            {
                bool[] cond = new bool[Params.Length];
                for (int i = 0; i < Params.Length; i++)
                    if (argTypes[i] == Args[i].ParameterType) cond[i] = true;
                if (cond[0] == true & cond[1] == true)
                    return node.Invoke(Params);
            }
        }
        return null;
    }

This is what the Params look like in the array [0] {Name = "IPAddress" FullName = "System.Net.IPAddress"} [1] 80 这就是参数在数组[0] {Name =“ IPAddress” FullName =“ System.Net.IPAddress”}中的样子[1] 80

this is the calling code, prob should have provided it before so you know what I'm trying to do as you can see it parses string values that represent classes, this is why I can't use typeof or typeconstraints. 这是调用代码,prob应该已经提供了它,所以您知道我要尝试执行的操作,因为您可以看到它解析表示类的字符串值,这就是为什么我不能使用typeof或typeconstraints的原因。

private object CreateInstance(ObjectCreationExpression Exp)
{
    object context = GetContext(Exp.Identifier); //Gets the class type
    Type t = (Type)context;
    object[] Params = GetParams(Exp.ArgumentList).ToArray();
    object newObject = Activator.CreateInstance(t, Params);
    return newObject;
}

public static object GetContext(string classname)
{

    return ParseNamespace("System.dll", classname);
}

private static object ParseNamespace(string Namespace, string classname) //Looks up class in System.dll
{
    string DotNetPath = ToolLocationHelper.GetPathToDotNetFramework(TargetDotNetFrameworkVersion.VersionLatest);
    Assembly Asm = Assembly.LoadFile(DotNetPath + @"\" + Namespace);
    Type[] Types = Asm.GetExportedTypes();
    foreach (Type Node in Types)
    {
        if (Node.Name == classname)
            return Node;
    }
    return null;
}

private List<object> GetParams(NodeCollection<ArgumentNode> Params)
{
    List<object> Arguments = new List<object>();
    foreach (ArgumentNode node in Params)
    {

        if (node.Expression is MemberAccessExpression)
        {
            MemberAccessExpression exp = (MemberAccessExpression)node.Expression;
            Type value = (Type)GetContext(exp);
            string name = DirectCast<IdentifierExpression>(exp.Right).Identifier;
            if (value.IsEnum)
            {
                string[] names = DirectCast<Type>(value).GetEnumNames();
                Array item = DirectCast<Type>(value).GetEnumValues();
                Arguments.Add(item.GetValue(names.ToList().IndexOf(name)));
            }
            else
            {
                Type item = value.GetMember(name)[0].ReflectedType;
                Arguments.Add(item);
            }
        }
        else
            Arguments.Add((Int32)ParseType(node.Expression));
    }
    return Arguments;
}

ObjectCreationExpression is a custom class that contains parsed sourcecode for creating a new instance, the two main properties are ArgumentList which is a collection of values or identifiers to be used as parameters, the other property is an identifier for the type we are creating ObjectCreationExpression是一个自定义类,其中包含用于创建新实例的已解析源代码,两个主要属性是ArgumentList,它是用作参数的值或标识符的集合,另一个属性是我们正在创建的类型的标识符

You have wrote a nice implementation to create object instance, however it had some flaws. 您已经编写了一个不错的实现来创建对象实例,但是它存在一些缺陷。 I've corrected them in the code below 我已经在下面的代码中更正了它们

    public static object CreateInstance(Type context, params object[] Params) // params keyword for array
    {
        List<Type> argTypes = new List<Type>();

        //used .GetType() method to get the appropriate type
        //Param can be null so handle accordingly
        foreach (object Param in Params)
            argTypes.Add((Param ?? new object()).GetType());
        ConstructorInfo[] Types = context.GetConstructors();
        foreach (ConstructorInfo node in Types)
        {
            ParameterInfo[] Args = node.GetParameters();
            if (Params.Length == Args.Length)
            {
                bool[] cond = new bool[Params.Length];
                //handle derived types
                for (int i = 0; i < Params.Length; i++)
                    if (Args[i].ParameterType.IsAssignableFrom(argTypes[i])) cond[i] = true;
                if (cond[0] && cond[1])
                    return node.Invoke(Params);
            }
        }
        return null;
    }
  • paramaters were not an array 参数不是数组
  • Param.GetType() is more appropriate Param.GetType()更合适
  • handle parameter of derived types(maybe buggy at this moment as value types and class type need to be differentiated) 处理派生类型的参数(由于值类型和类类型需要区分,此刻可能有问题)

calling code 呼叫码

IPEndPoint newObject = (IPEndPoint)CreateInstance(typeof(IPEndPoint), IPAddress.Any, 80);

Note I may not be able to correct every flaw in the sample above, I just made it workable for your scenario ie you calling code 注意我可能无法纠正上面示例中的每个缺陷,只是使它适用于您的情况,即您调用代码

Generics implementation 泛型实现

    public static T CreateInstance<T>(params object[] Params) where T : class // params keyword for array
    {
        List<Type> argTypes = new List<Type>();

        //used .GetType() method to get the appropriate type
        //Param can be null so handle accordingly
        foreach (object Param in Params)
            argTypes.Add((Param ?? new object()).GetType());
        ConstructorInfo[] Types = typeof(T).GetConstructors();
        foreach (ConstructorInfo node in Types)
        {
            ParameterInfo[] Args = node.GetParameters();
            if (Params.Length == Args.Length)
            {
                bool[] cond = new bool[Params.Length];
                //handle derived types
                for (int i = 0; i < Params.Length; i++)
                    if (Args[i].ParameterType.IsAssignableFrom(argTypes[i])) cond[i] = true;
                if (cond[0] && cond[1])
                    return (T)node.Invoke(Params);
            }
        }
        return default(T);
    }

calling code 呼叫码

IPEndPoint newObject = CreateInstance<IPEndPoint>(IPAddress.Any, 80);

Fully dynamic object construction 完全动态的对象构造

    public static object CreateInstance(Type pContext, object[] Params)
    {
        List<Type> argTypes = new List<Type>();

        //used .GetType() method to get the appropriate type
        //Param can be null so handle accordingly
        if (Params != null)
            foreach (object Param in Params)
            {
                if (Param != null)
                    argTypes.Add(Param.GetType());
                else
                    argTypes.Add(null);
            }

        ConstructorInfo[] Types = pContext.GetConstructors();
        foreach (ConstructorInfo node in Types)
        {
            ParameterInfo[] Args = node.GetParameters();
            // Params can be null for default constructors so use argTypes
            if (argTypes.Count == Args.Length)
            {
                bool areTypesCompatible = true;
                for (int i = 0; i < Params.Length; i++)
                {
                    if (argTypes[i] == null)
                    {
                        if (Args[i].ParameterType.IsValueType)
                        {
                            //fill the defaults for value type if not supplied
                            Params[i] = CreateInstance(Args[i].ParameterType, null);
                            argTypes[i] = Params[i].GetType();
                        }
                        else
                        {
                            argTypes[i] = Args[i].ParameterType;
                        }
                    }
                    if (!Args[i].ParameterType.IsAssignableFrom(argTypes[i]))
                    {
                        areTypesCompatible = false;
                        break;
                    }
                }
                if (areTypesCompatible)
                    return node.Invoke(Params);
            }
        }

        //delegate type to Activator.CreateInstance if unable to find a suitable constructor
        return Activator.CreateInstance(pContext);
    }

calling code 呼叫码

IPEndPoint newObject = (IPEndPoint)CreateInstance(typeof(IPEndPoint), new object[] { IPAddress.Any, 80});

this code can also null parameters 此代码还可以将参数设为空

eg 例如

IPEndPoint newObject = (IPEndPoint)CreateInstance(typeof(IPEndPoint), new object[] { IPAddress.Any, null});

I simplified it a bit and also handled null parameters for default constructors also. 我对其进行了简化,还为默认构造函数处理了空参数。 and couple of other checks 和其他几张支票

so this change makes it complete dynamic even you can construct value types too 因此,即使您也可以construct value types ,此更改也使其完全动态

eg 例如

int obj = (int)CreateInstance(typeof(int), null);

Example for your case 您的案例

object context = GetContext(Exp.Identifier);
Type t = (Type)context;
object[] Params = GetParams(Exp.ArgumentList).ToArray();
//use the above defined method and it will work as expected
object newObject = CreateInstance(t, Params);

For what it's worth, this is my refactoring of your method: 对于它的价值,这是我对您的方法的重构:

public static object CreateInstance(Type pContext, params object[] pArguments) {
   var constructors = pContext.GetConstructors();

   foreach (var constructor in constructors) {
      var parameters = constructor.GetParameters();
      if (parameters.Length != pArguments.Length)
         continue;

      // assumed you wanted a matching constructor
      // not just one that matches the first two types
      bool fail = false;
      for (int x = 0; x < parameters.Length && !fail; x++)
         if (!parameters[x].ParameterType.IsInstanceOfType(pArguments[x]))
            fail = true;

      if (!fail)
         return constructor.Invoke(pArguments);
   }
   return null;
}

Note that you seem to have the notion of "parameter" and "argument" backwards. 请注意,您似乎反过来具有“参数”和“参数”的概念。 A "parameter" is the named part of the method that accepts a value. “参数”是接受值的方法的命名部分。 An "argument" is the actual value that you pass. “参数”是您传递的实际值。

Aside from that, it sounds like your problem has more to do with the values you are passing, than the implementation of the method. 除此之外,听起来您的问题更多地与传递的值有关,而不是方法的实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM