Ich habe eine Methode, mit der ich den Typ einer Klasse auflösen muss. Diese Klasse existiert in einer anderen Assembly mit dem Namespace ähnlich:
MyProject.Domain.Model
Ich versuche Folgendes auszuführen:
Type.GetType("MyProject.Domain.Model." + myClassName);
Dies funktioniert hervorragend, wenn sich der Code, der diese Aktion ausführt, in derselben Assembly befindet wie die Klasse, deren Typ ich auflösen möchte. Wenn sich meine Klasse jedoch in einer anderen Assembly befindet, schlägt dieser Code fehl.
Ich bin mir sicher, dass es einen weitaus besseren Weg gibt, um diese Aufgabe zu erfüllen, aber ich habe nicht viel Erfahrung mit dem Auflösen von Assemblys und dem Durchsuchen von Namespaces, um den Typ der gesuchten Klasse aufzulösen. Irgendwelche Ratschläge oder Tipps, um diese Aufgabe eleganter zu erledigen?
c#
.net
reflection
Brandon
quelle
quelle
Antworten:
Sie müssen den Namen der Assembly wie folgt hinzufügen:
Type.GetType("MyProject.Domain.Model." + myClassName + ", AssemblyName");
Um Mehrdeutigkeiten zu vermeiden oder wenn sich die Baugruppe im GAC befindet, sollten Sie einen vollständig qualifizierten Baugruppennamen wie den folgenden angeben:
Type.GetType("System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
quelle
listType.MakeGenericType(itemType)
. Beide Typvariablen könnenType.GetType()
wie in meiner Antwort erstellt werden.Diese universelle Lösung ist für Personen gedacht, die generische Typen aus dynamischen externen Referenzen von laden müssen
AssemblyQualifiedName
, ohne zu wissen, von welcher Assembly alle Teile des generischen Typs stammen: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; }
Und Sie können es mit diesem Code (Konsolen-App) testen :
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(); }
Ich teile meine Lösung, um Menschen mit dem gleichen Problem wie mir zu helfen (um JEDEN Typ aus einer Zeichenfolge zu deserialisieren, die in einer extern referenzierten Assembly sowohl teilweise als auch als Ganzes definiert werden kann - und die Referenzen werden vom Benutzer der App dynamisch hinzugefügt).
Hoffe es hilft jedem!
quelle
Ähnlich wie beim OP musste ich eine begrenzte Teilmenge von Typen nach Namen laden (in meinem Fall befanden sich alle Klassen in einer einzigen Assembly und implementierten dieselbe Schnittstelle). Ich hatte viele seltsame Probleme, als ich versuchte, sie
Type.GetType(string)
gegen eine andere Assembly zu verwenden (sogar das Hinzufügen des AssemblyQualifiedName, wie in anderen Beiträgen erwähnt). So habe ich das Problem gelöst:Verwendung:
var mytype = TypeConverter<ICommand>.FromString("CreateCustomer");
Code:
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); } } } }
Natürlich können Sie die CacheTypes-Methode optimieren, um alle Assemblys in der AppDomain oder eine andere Logik zu überprüfen, die besser zu Ihrem Anwendungsfall passt. Wenn in Ihrem Anwendungsfall das Laden von Typen aus mehreren Namespaces möglich ist, möchten Sie möglicherweise den Wörterbuchschlüssel ändern, um
FullName
stattdessen die Typen zu verwenden. Oder wenn Ihre Typen nicht von einer gemeinsamen Schnittstelle oder Basisklasse erben, können Sie<BaseType>
die CacheTypes-Methode entfernen und ändern, um so etwas wie zu verwenden.GetTypes().Where(t => t.Namespace.StartsWith("MyProject.Domain.Model.")
quelle
Laden Sie zuerst die Baugruppe und dann den Typ. Beispiel: Assembly DLL = Assembly.LoadFile (PATH); DLL.GetType (typeName);
quelle
Können Sie eine der Standardmethoden verwenden?
typeof( MyClass ); MyClass c = new MyClass(); c.GetType();
Wenn nicht, müssen Sie dem Typ.GetType Informationen zur Assembly hinzufügen.
quelle
Type.GetType(Type.GetType("MyProject.Domain.Model." + myClassName).AssemblyQualifiedName)
quelle