【问题标题】:Resolve Type from Class Name in a Different Assembly从不同程序集中的类名中解析类型
【发布时间】:2011-03-31 13:13:55
【问题描述】:

我有一个方法需要解析类的类型。该类存在于另一个程序集中,其命名空间类似于:

MyProject.Domain.Model

我正在尝试执行以下操作:

Type.GetType("MyProject.Domain.Model." + myClassName);

如果执行此操作的代码与我尝试解析其类型的类位于同一程序集中,则此方法非常有用,但是,如果我的类位于不同的程序集中,则此代码将失败。

我确信有更好的方法来完成这项任务,但我在解析程序集和遍历其中的命名空间以解析我正在寻找的类的类型方面没有很多经验。有什么建议或技巧可以更优雅地完成这项任务吗?

【问题讨论】:

标签: c# .net reflection


【解决方案1】:

先加载程序集,再加载类型。

例子:

Assembly DLL = Assembly.LoadFile(PATH);
DLL.GetType(typeName);

【讨论】:

    【解决方案2】:

    与 OP 类似,我需要按名称加载有限的类型子集(在我的情况下,所有类都在一个程序集中并实现了相同的接口)。在尝试对不同的程序集使用 Type.GetType(string) 时,我遇到了很多奇怪的问题(甚至添加了其他帖子中提到的 AssemblyQualifiedName)。以下是我解决问题的方法:

    用法:

    var mytype = TypeConverter<ICommand>.FromString("CreateCustomer");
    

    代码:

        public class TypeConverter<BaseType>
            {
                private static Dictionary<string, Type> _types;
                private static object _lock = new object();
    
                public static Type FromString(string typeName)
                {
                    if (_types == null) CacheTypes();
    
                    if (_types.ContainsKey(typeName))
                    {
                        return _types[typeName];
                    }
                    else
                    {
                        return null;
                    }
                }
    
                private static void CacheTypes()
                {
                    lock (_lock)
                    {
                        if (_types == null)
                        {
                            // Initialize the myTypes list.
                            var baseType = typeof(BaseType);
                            var typeAssembly = baseType.Assembly;
                            var types = typeAssembly.GetTypes().Where(t => 
                                t.IsClass && 
                                !t.IsAbstract && 
                                baseType.IsAssignableFrom(t));
    
                            _types = types.ToDictionary(t => t.Name);
                        }
                    }
                }
            }
    

    显然,您可以调整 CacheTypes 方法来检查 AppDomain 中的所有程序集,或其他更适合您的用例的逻辑。如果您的用例允许从多个命名空间加载类型,您可能希望更改字典键以改用该类型的 FullName。或者,如果您的类型不是从通用接口或基类继承的,您可以删除 &lt;BaseType&gt; 并更改 CacheTypes 方法以使用类似 .GetTypes().Where(t =&gt; t.Namespace.StartsWith("MyProject.Domain.Model.")

    【讨论】:

      【解决方案3】:

      此通用解决方案适用于需要通过AssemblyQualifiedName从动态外部引用中加载泛型类型,但不知道泛型类型的所有部分来自哪个程序集的人来自:

          public static Type ReconstructType(string assemblyQualifiedName, bool throwOnError = true, params Assembly[] referencedAssemblies)
          {
              foreach (Assembly asm in referencedAssemblies)
              {
                  var fullNameWithoutAssemblyName = assemblyQualifiedName.Replace($", {asm.FullName}", "");
                  var type = asm.GetType(fullNameWithoutAssemblyName, throwOnError: false);
                  if (type != null) return type;
              }
      
              if (assemblyQualifiedName.Contains("[["))
              {
                  Type type = ConstructGenericType(assemblyQualifiedName, throwOnError);
                  if (type != null)
                      return type;
              }
              else
              {
                  Type type = Type.GetType(assemblyQualifiedName, false);
                  if (type != null)
                      return type;
              }
      
              if (throwOnError)
                  throw new Exception($"The type \"{assemblyQualifiedName}\" cannot be found in referenced assemblies.");
              else
                  return null;
          }
      
          private static Type ConstructGenericType(string assemblyQualifiedName, bool throwOnError = true)
          {
              Regex regex = new Regex(@"^(?<name>\w+(\.\w+)*)`(?<count>\d)\[(?<subtypes>\[.*\])\](, (?<assembly>\w+(\.\w+)*)[\w\s,=\.]+)$?", RegexOptions.Singleline | RegexOptions.ExplicitCapture);
              Match match = regex.Match(assemblyQualifiedName);
              if (!match.Success)
                  if (!throwOnError) return null;
                  else throw new Exception($"Unable to parse the type's assembly qualified name: {assemblyQualifiedName}");
      
              string typeName = match.Groups["name"].Value;
              int n = int.Parse(match.Groups["count"].Value);
              string asmName = match.Groups["assembly"].Value;
              string subtypes = match.Groups["subtypes"].Value;
      
              typeName = typeName + $"`{n}";
              Type genericType = ReconstructType(typeName, throwOnError);
              if (genericType == null) return null;
      
              List<string> typeNames = new List<string>();
              int ofs = 0;
              while (ofs < subtypes.Length && subtypes[ofs] == '[')
              {
                  int end = ofs, level = 0;
                  do
                  {
                      switch (subtypes[end++])
                      {
                          case '[': level++; break;
                          case ']': level--; break;
                      }
                  } while (level > 0 && end < subtypes.Length);
      
                  if (level == 0)
                  {
                      typeNames.Add(subtypes.Substring(ofs + 1, end - ofs - 2));
                      if (end < subtypes.Length && subtypes[end] == ',')
                          end++;
                  }
      
                  ofs = end;
                  n--;  // just for checking the count
              }
      
              if (n != 0)
                  // This shouldn't ever happen!
                  throw new Exception("Generic type argument count mismatch! Type name: " + assemblyQualifiedName);  
      
              Type[] types = new Type[typeNames.Count];
              for (int i = 0; i < types.Length; i++)
              {
                  try
                  {
                      types[i] = ReconstructType(typeNames[i], throwOnError);
                      if (types[i] == null)  // if throwOnError, should not reach this point if couldn't create the type
                          return null;
                  }
                  catch (Exception ex)
                  {
                      throw new Exception($"Unable to reconstruct generic type. Failed on creating the type argument {(i + 1)}: {typeNames[i]}. Error message: {ex.Message}");
                  }
              }
      
              Type resultType = genericType.MakeGenericType(types);
              return resultType;
          }
      

      并且您可以使用此代码对其进行测试(控制台应用程序):

          static void Main(string[] args)
          {
              Type t1 = typeof(Task<Dictionary<int, Dictionary<string, int?>>>);
              string name = t1.AssemblyQualifiedName;
              Console.WriteLine("Type: " + name);
              // Result: System.Threading.Tasks.Task`1[[System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
              Type t2 = ReconstructType(name);
              bool ok = t1 == t2;
              Console.WriteLine("\r\nLocal type test OK: " + ok);
      
              Assembly asmRef = Assembly.ReflectionOnlyLoad("System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
              // Task<DialogResult> in refTypeTest below:
              string refTypeTest = "System.Threading.Tasks.Task`1[[System.Windows.Forms.DialogResult, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
              Type t3 = ReconstructType(refTypeTest, true, asmRef);
              Console.WriteLine("External type test OK: " + (t3.AssemblyQualifiedName == refTypeTest));
      
              // Getting an external non-generic type directly from references:
              Type t4 = ReconstructType("System.Windows.Forms.DialogResult, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", true, asmRef);
      
              Console.ReadLine();
          }
      

      我正在分享我的解决方案,以帮助与我有同样问题的人(从可以在外部引用程序集中部分或整体定义的字符串中反序列化任何类型 - 并且引用由应用程序的用户动态添加) .

      希望对大家有所帮助!

      【讨论】:

        【解决方案4】:

        使用AssemblyQualifiedName 属性的短而动态的方法-

        Type.GetType(Type.GetType("MyProject.Domain.Model." + myClassName).AssemblyQualifiedName)
        

        享受吧!

        【讨论】:

        • 如果 Type.GetType("MyProject.Domain.Model." + myClassName) 失败,如何将其包装在另一个 GetType 调用中防止这种情况发生?
        • 在任何情况下,您都可以将其包装在带有 System.NullReferenceException 的 try catch 块中。更可能在此错误 - “MyProject.Domain.Model.ClassName, ClassName, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089” 然后在此 - “MyProject.Domain.Model.”...
        • @Kaine 我不确定 simonbor 是什么意思,但是如果您在编写字符串时使用 GetType().AssemblyQualifiedName,那么在使用字符串解析类型时就不必担心它。
        【解决方案5】:

        你可以使用任何一种标准方式吗?

        typeof( MyClass );
        
        MyClass c = new MyClass();
        c.GetType();
        

        如果没有,您必须向 Type.GetType 添加有关程序集的信息。

        【讨论】:

          【解决方案6】:

          您必须像这样添加程序集名称:

          Type.GetType("MyProject.Domain.Model." + myClassName + ", AssemblyName");
          

          为避免歧义,或者如果程序集位于 GAC 中,您应该提供一个完全限定的程序集名称,如下所示:

          Type.GetType("System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
          

          【讨论】:

          • 太好了,我知道我错过了一些小东西,比如包括程序集。该解决方案满足我的需求。谢谢。
          • 对于那些处理序列化的人:要获得程序集限定名称,有属性Type.AssemblyQualifiedName
          • 如果类型是 List ,其中 T 是自定义类,您如何指定 2 个程序集?即 System.Collections.Generic.List 的 mscorlib 程序集,以及包含 T? 的库
          • @SimonGreen:您可能必须使用listType.MakeGenericType(itemType) 来构建它。两种类型变量都可以使用Type.GetType() 构造,就像我的回答一样。
          • object.Assembly.ToString() 也可以用来获取完整的程序集。
          猜你喜欢
          • 1970-01-01
          • 2012-04-23
          • 1970-01-01
          • 2017-01-30
          • 1970-01-01
          • 2012-02-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多