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

时间:2016-05-17 20:16:28

标签: c# string reflection casting

想象一下以下场景:

用户将字符串传递给应用程序,该应用程序代表.NET类型,例如stringSystem.IntPtr。假设有问题的应用程序可以访问定义了给定类型的程序集,它可以根据字符串创建给定类型的实例。

我设法创建了这样一个应用程序;问题是,我还没有(还)能够创建“复杂”“嵌套”类型,例如:

  • System.Tuple<string, System.Action>
  • 这样的泛型
  • T的数组int[]System.IntPtr[]等......
  • T的指针,如int*char**Namespace.MyStruct*等......
  • 上述案例的任何(可能)组合

我的问题是:如果只给出了类型的字符串表示(当然还有所有必需的程序集),我怎样才能创建这样的嵌套类型实例?

.NET BCL是否包含某种string --> Type - 解析器?或者我是否必须使用某种形式的递归正则表达式解析器和Activator.CreateInstance

编辑№1:
由于这个问题似乎过于宽泛,我会试着澄清一下自己:

我能够为“简单”类型创建类型实例 - 但是,我正在努力创建“复杂”类型的类型实例,例如:

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

obj1string的一个实例,但obj2会失败,因为变量t2null(意思是,第二行是给定的代码无法找到“复杂”类型。)

编辑№2:
Type::MakeGenericType(Type[])的用法显然很有用,但是如何创建类似这样的类型的实例最简单/最快(?)方式:
IEnumerable<Tuple<int[,], char[][], Dictionary<string, Func<int, int*, byte>>>>
我必须编写一些基于正则表达式的解析器,它在给定字符串的不同部分递归迭代....

编辑№3:
我发现了System::CodeDom::CodeTypeReference,我正在寻找一种方法来获取基于给定CodeTypeReference - 实例的类型(可以使用类型的字符串表示创建)。

编辑№4: (抱歉所有修改)
我想我在this SO post找到了我的解决方案。

2 个答案:

答案 0 :(得分:2)

我可能会遗漏一些东西,但看起来你正在寻找Reflection。具体来说,GetType方法。该文档说明了以下有关此方法的内容:

  

获取具有指定名称的Type,执行区分大小写的搜索。

然后你会想看看Activator.CreateInstance

  

使用该类型的默认构造函数

创建指定类型的实例

在为通用类型创建实例之前,您需要查看`Type.MakeGenericType'

  

为当前泛型类型定义的类型参数替换类型数组的元素,并返回表示结果构造类型的Type对象。

您可能会发现其他问题的答案也很有用:How to use Activator to create an instance of a generic Type and casting it back to that type?

答案 1 :(得分:1)

我解决了(暂时)如下:

  1. 检查给定字符串是否包含<>
  2. 如果是,请根据下面给出的函数解析字符串(ParseGeneric)并使用解析的标记继续递归
  3. 如果没有,请检查是否有任何数组括号([][][][,]等。)
  4. 使用递归Array::CreateInstance(Type,int[])
  5. 相应地解析类型
  6. 检查是否出现任何指针'星'(*)并使用Marshal::StructureToPtr
  7. 解析相应类型

    <小时/> 我的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);
    }
    

    函数FetchType执行一些基本的数组括号解析,然后通过查看给定的程序集,根据结果字符串获取类型。