#c# #reflection #activator
#c# #отражение #активатор
Вопрос:
Привет, я пытаюсь выполнить следующее динамически, я использую свой собственный метод CreateInstance, но это было протестировано с помощью Activator.CreateInstance
IPEndPoint newObject = new IPEndPoint(IPAddress.Any, 80);
когда я пытаюсь использовать activator, я получаю ошибку, не удается преобразовать 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 amp; cond[1] == true)
return node.Invoke(Params);
}
}
return null;
}
Вот как выглядят параметры в массиве
[0] {Name = «IPAddress» FullName = «System.Net .IP-адрес»}
[1] 80
это вызывающий код, 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 — это пользовательский класс, который содержит проанализированный исходный код для создания нового экземпляра. двумя основными свойствами являются ArgumentList, который представляет собой набор значений или идентификаторов, которые будут использоваться в качестве параметров, другое свойство — это идентификатор типа, который мы создаем
Комментарии:
1. Вы передаете
Type
объект, а не фактическийIPAddress
, в качестве первого параметра. Не видя вызывающего кода, мы не можем узнать, как сложилось такое положение дел.2. Вы говорите, что получаете ошибку «невозможно преобразовать» при использовании
Activator.CreateInstance
, но вы не сказали, в чем проблема с вашимCreateInstance
методом.
Ответ №1:
Вы написали хорошую реализацию для создания экземпляра объекта, однако у нее были некоторые недостатки. Я исправил их в приведенном ниже коде
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] amp;amp; cond[1])
return node.Invoke(Params);
}
}
return null;
}
- параметры не были массивом
- Параметр.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] amp;amp; 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);
Комментарии:
1. Избавьтесь от
if(cond[0] == true...
проверки. Похоже, это отладочный код. Это не может быть правильным для создания объектов общего назначения.2. Спасибо за ваши ответы, к сожалению, я не могу использовать ограничения типа или типа <>, потому что это должно быть динамическим и будет использоваться в других классах, а не только в IPAddress .
3. Я надеюсь, что вы можете передать любой класс, используя его, он не ограничен IPAddress или любой другой вещью. однако для полной динамики вы можете изменить параметры
(object[] Params)
и передать параметры следующимnew object[]{ IPAddress.Any, 80}
образом. Я могу создать образец, если вы этого требуете.4. @pushpraj, если это попытка создания универсальной
CreateInstance
реализацииif (cond[0] amp;amp; cond[1])
, проверка кажется ужасно специфичной для конкретного случая.5. Также вы проверяете, можно ли назначить конкретный параметр из данного типа аргумента (вы используете эти термины в обратном порядке), но тип аргумента эффективно изменяется с помощью
Param ?? new object()
.null
было бы вполне допустимым аргументом для конструктора, который имеет параметр типаstring
, например, ноnew object()
— не так много.
Ответ №2:
Для чего это стоит, это мой рефакторинг вашего метода:
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 amp;amp; !fail; x )
if (!parameters[x].ParameterType.IsInstanceOfType(pArguments[x]))
fail = true;
if (!fail)
return constructor.Invoke(pArguments);
}
return null;
}
Обратите внимание, что у вас, похоже, есть понятия «параметр» и «аргумент» наоборот. «Параметр» — это именованная часть метода, которая принимает значение. «Аргумент» — это фактическое значение, которое вы передаете.
Кроме того, похоже, что ваша проблема больше связана с передаваемыми вами значениями, чем с реализацией метода.