Activator.CreateInstance, вызывающий конструктор с классом в качестве параметра

Привет я пытаюсь сделать следующее динамически, я использую свой собственный метод CreateInstance, но это было проверено с Activator.CreateInstance

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

когда я пытаюсь использовать активатор, я получаю сообщение об ошибке, не могу преобразовать System.RuntimeType в IP-адрес

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

Вот как выглядят параметры в массиве [0] {Name = "IPAddress" FullName = "System.Net.IPAddress"} [1] 80

это вызывающий код, вероятно, он должен был предоставить его раньше, чтобы вы знали, что я пытаюсь сделать, поскольку вы можете видеть, как он анализирует строковые значения, представляющие классы, поэтому я не могу использовать 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 - это пользовательский класс, который содержит разобранный исходный код для создания нового экземпляра, два основных свойства - ArgumentList, который представляет собой набор значений или идентификаторов, которые будут использоваться в качестве параметров, другое свойство - это идентификатор для создаваемого нами типа.

2 ответа

Вы написали хорошую реализацию для создания экземпляра объекта, однако у него были некоторые недостатки. Я исправил их в коде ниже

    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;
    }
  • параметры не были массивом
  • Param.GetType() является более подходящим
  • обрабатывать параметры производных типов (возможно, глючит в данный момент, поскольку необходимо различать типы значений и типы классов)

код вызова

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

Примечание. Возможно, я не смогу исправить каждый недостаток в приведенном выше примере, я просто сделал его работоспособным для вашего сценария, т.е. вы вызываете код

Реализация дженериков

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

код вызова

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

Полностью динамическое строительство объекта

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

код вызова

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

этот код также может иметь нулевые параметры

например

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

Я немного упростил это, а также обработал нулевые параметры для конструкторов по умолчанию. и пару других проверок

так что это изменение делает его полностью динамичным, даже если вы можете construct value types тоже

например

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

Пример для вашего случая

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

Для чего это стоит, это мой рефакторинг вашего метода:

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

Обратите внимание, что вы, кажется, имеете понятие "параметр" и "аргумент" в обратном направлении. "Параметр" - это именованная часть метода, которая принимает значение. "Аргумент" - это фактическое значение, которое вы передаете.

Кроме того, похоже, что ваша проблема больше связана с передаваемыми значениями, чем с реализацией метода.

Другие вопросы по тегам