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?
Use Type. GetType to get the Type objects from an assembly that is already loaded.
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.
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");
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With