简体   繁体   English

通过反射从其字符串创建任何类型实例

[英]Create any type instance from its string via reflection

Imagine the following scenario: 想象以下情况:

A user passes a string to the application, which represents a .NET-Type, eg string or System.IntPtr . 用户将代表.NET类型的字符串传递给应用程序,例如stringSystem.IntPtr Assuming, that the application in question has access to the assemblies, in which the given Types are defined, it can create an instance of the given type based on the string. 假设所讨论的应用程序可以访问在其中定义了给定类型的程序集,则可以基于字符串创建给定类型的实例。

I have managed to create such an application; 我已经设法创建了这样的应用程序; the problem is only, that I am not (yet) able to create "complex" or "nested" types, eg: 问题仅仅是,我还不能创建“复杂”“嵌套”类型,例如:

  • Generics like System.Tuple<string, System.Action> 诸如System.Tuple<string, System.Action>类的泛型
  • Arrays of T like int[] , System.IntPtr[] , etc... T数组,例如int[]System.IntPtr[]等。
  • Pointer of T like int* , char** , Namespace.MyStruct* , etc... T指针,例如int*char**Namespace.MyStruct*等。
  • Any (possible) combination of the cases above 上述情况的任何(可能)组合

My question is: How can I create such nested type instances, when only given the string representation of the type (and of course all required assemblies)? 我的问题是:仅给出类型的字符串表示形式(当然还有所有必需的程序集)时,如何创建此类嵌套类型实例?

Do the .NET BCL contain some sort of string --> Type -parser? .NET BCL是否包含某种string --> Type -parser? Or do I have to use some form of recursive regex-parser and Activator.CreateInstance ? 还是我必须使用某种形式的递归regex-parser和Activator.CreateInstance


EDIT №1: 编辑№1:
As this question seems to be too broad, I will try to clarify myself: 由于这个问题似乎太广泛了,我将尝试澄清一下自己:

I am able to create type instances for 'simple' types - I am, however, struggling to create type instances of 'complex' types, eg: 我能够为“简单”类型创建类型实例-但是,我正在努力创建“复杂”类型的类型实例,例如:

Type t1 = typeof(string).Assembly.GetType("System.String");
Type t2 = typeof(string).Assembly.GetType("System.Tuple<System.String, System.Int32>");
object obj1 = Activator.CreateInstance(t1);
object obj2 = Activator.CreateInstance(t2);

obj1 is an instance of string , but obj2 will fail, as the variable t2 is null (Meaning, that the second line in the given code was unable to find the 'complex' type). obj1string一个实例,但是obj2将失败,因为变量t2null (意味着,给定代码中的第二行无法找到“复杂”类型)。


EDIT №2: 编辑№2:
The usage of Type::MakeGenericType(Type[]) is obviously helpful, but how would be the easiest/fastest(?) way to create an instance of a type like this: Type::MakeGenericType(Type[])的使用显然很有帮助,但是创建这种类型的实例的最简单/最快(?)方式将是怎样的:
IEnumerable<Tuple<int[,], char[][], Dictionary<string, Func<int, int*, byte>>>>
I would have to write some kind of regex-based parser, which iterates recursively over the different sections of the given string.... 我将不得不编写某种基于正则表达式的解析器,该解析器在给定字符串的不同部分上进行递归迭代。


EDIT №3: 编辑№3:
I have discovered System::CodeDom::CodeTypeReference and I am currently searching for a method to get the type based on a given CodeTypeReference -instance (which can be created using the type's string representation). 我发现了System::CodeDom::CodeTypeReference并且我目前正在寻找一种基于给定CodeTypeReference -instance(可以使用类型的字符串表示形式创建)来获取类型的方法。


EDIT №4: (sorry for all the edits) 编辑№4:( 对不起所有编辑)
I think I found my solution in this SO post . 我想我在这篇SO帖子中找到了解决方案。

I might be missing something, but it looks like you're looking for Reflection . 我可能会丢失一些东西,但是看起来您在寻找Reflection Specifically, the GetType method. 具体来说,就是GetType方法。 The documentation states the following about this method: 文档指出有关此方法的以下内容:

Gets the Type with the specified name, performing a case-sensitive search. 获取具有指定名称的Type,执行区分大小写的搜索。

Then you will want to look at Activator.CreateInstance : 然后,您将要查看Activator.CreateInstance

Creates an instance of the specified type using that type's default constructor 使用该类型的默认构造函数创建指定类型的实例

Before you create the instance for generic types, though, you'll be wanting to look at `Type.MakeGenericType' : 但是,在为泛型类型创建实例之前,您将需要查看`Type.MakeGenericType'

Substitutes the elements of an array of types for the type parameters of the current generic type definition and returns a Type object representing the resulting constructed type. 将类型数组的元素替换为当前泛型类型定义的类型参数,并返回表示所得构造类型的Type对象。

You might find the answers to this other question useful too: How to use Activator to create an instance of a generic Type and casting it back to that type? 您可能还会发现其他问题的答案也很有用: 如何使用Activator创建通用类型的实例并将其强制转换回该类型?

I solved it (temporarily) as follows: 我(暂时)解决了以下问题:

  1. Check if the given string contains < or > 检查给定的字符串是否包含<>
  2. If so, parse the string according to the function given below ( ParseGeneric ) and continue recursively using the parsed tokens 如果是这样,请根据下面提供的函数( ParseGeneric )解析字符串,然后继续使用解析的标记递归
  3. If not, check for any occurrence of array brackets ( [] , [][] , [,] etc.) 如果不是,请检查是否出现了数组括号( [][][][,]等)
  4. Parse the type accordingly using a recursive Array::CreateInstance(Type,int[]) 使用递归Array::CreateInstance(Type,int[])相应地解析类型
  5. Check for any occurrence of pointer 'stars' ( * ) and parse the type accordingly using Marshal::StructureToPtr 检查是否存在任何指针'stars'( * ),并使用Marshal::StructureToPtr相应地解析类型。


My ParseGeneric -method: 我的ParseGeneric方法:

 public static Type FetchGenericType(string typestring) { Match m; if ((m = Regex.Match(typestring, )).Success) { string cls = m.Groups["class"].ToString(); string par = m.Groups["params"].Success ? m.Groups["params"].ToString() : ""; List<string> paramtypes = new List<string>(); int s = 0, e = 0; for (int i = 0, c = 0, l = par.Length; i < l; i++) switch (par[i]) { case ',': if (c > 0) goto default; paramtypes.Add(par.Substring(s, e)); s = i + 1; e = 0; break; case '<': ++c; goto default; case '>': --c; goto default; default: ++e; break; } // I know, that this is bad as hell, but what should I do instead? paramtypes.Add(par.Substring(s, e)); IEnumerable<Type> @params = from type in paramtypes where !string.IsNullOrWhiteSpace(type) select FetchGenericType(type); string paramstring = string.Join(", ", from type in @params select "[" + type.FullName + ", " + type.Assembly.GetName().Name + "]"); string result = @params.Count() == 0 ? cls : string.Format("{0}`{1}[{2}]", cls, @paramsCount(), paramstr); // The string has now the format '...List`1[[System.String, mscorlib]]' // or: 'System.Tuple[[System.Int32, mscorlib], [System.Object, mscorlib]]' ... return FetchType(result); } else return FetchType(typestring); } 

The function FetchType does some basic array bracket parsing and then fetches the type based on the resulted string by looking through the given assemblies. 函数FetchType执行一些基本的数组括号解析,然后通过查看给定的程序集,基于结果字符串获取类型。

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

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