Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolve Type from Class Name in a Different Assembly

I have a method where I need to resolve the Type of a class. This class exists in another assembly with the namespace similar to:

MyProject.Domain.Model 

I am attempting to perform the following:

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

This works great if the code that is performing this action is in the same assembly as the class whose type I am trying to resolve, however, if my class is in a different assembly, this code fails.

I am sure there is a far better way to accomplish this task, but I have not had a lot of experience with resolving assemblies and traversing namespaces within to resolve the type of the class I am looking for. Any advice or tips to accomplish this task more gracefully?

like image 454
Brandon Avatar asked Aug 18 '10 13:08

Brandon


People also ask

How do you get type objects from assemblies that are already loaded?

Use Type. GetType to get the Type objects from an assembly that is already loaded.

How to Get type from assembly?

GetType(String, Boolean) Gets the Type object with the specified name in the assembly instance and optionally throws an exception if the type is not found.


2 Answers

You'll have to add the assembly name like this:

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

To avoid ambiguity or if the assembly is located in the GAC, you should provide a fully qualified assembly name like such:

Type.GetType("System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); 
like image 114
Sandor Drieënhuizen Avatar answered Sep 22 '22 09:09

Sandor Drieënhuizen


This universal solution is for people who need to load generic types from dynamic external references by AssemblyQualifiedName, without knowing from which assembly are all parts of generic type coming from:

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

And you can test it with this code (console app):

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

I'm sharing my solution to help people with the same problem as me (to deserialize ANY type from string that could be defined both partially or as a whole in externally referenced assembly - and the references are dynamically added by app's user).

Hope it helps anyone!

like image 45
P.W. Avatar answered Sep 24 '22 09:09

P.W.