Mam metodę, w której muszę rozwiązać typ klasy. Ta klasa istnieje w innym zestawie z przestrzenią nazw podobną do:
MyProject.Domain.Model
Próbuję wykonać następujące czynności:
Type.GetType("MyProject.Domain.Model." + myClassName);
Działa to świetnie, jeśli kod, który wykonuje tę akcję, znajduje się w tym samym zestawie, co klasa, której typ próbuję rozwiązać, jednak jeśli moja klasa znajduje się w innym zestawie, ten kod nie powiedzie się.
Jestem pewien, że istnieje znacznie lepszy sposób wykonania tego zadania, ale nie miałem zbyt dużego doświadczenia w rozwiązywaniu zestawów i przemierzaniu przestrzeni nazw w celu określenia typu klasy, której szukam. Jakieś rady lub wskazówki, jak wykonać to zadanie z większym wdziękiem?
c#
.net
reflection
Brandon
źródło
źródło
Odpowiedzi:
Będziesz musiał dodać nazwę zestawu w następujący sposób:
Type.GetType("MyProject.Domain.Model." + myClassName + ", AssemblyName");
Aby uniknąć niejednoznaczności lub jeśli zestaw znajduje się w GAC, należy podać w pełni kwalifikowaną nazwę zestawu, taką jak:
Type.GetType("System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
źródło
listType.MakeGenericType(itemType)
. Obie zmienne typu można skonstruować przy użyciu funkcjiType.GetType()
like w mojej odpowiedzi.To uniwersalne rozwiązanie jest dla ludzi, którzy potrzebują, aby załadować typy generyczne z dynamicznych odniesień zewnętrznych przez
AssemblyQualifiedName
nie wiedząc, z której zespół są wszystkie elementy typu rodzajowego pochodzące z: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; }
I można go przetestować z tym kodem (app konsoli):
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(); }
Dzielę się moim rozwiązaniem, aby pomóc ludziom z tym samym problemem co ja (aby deserializować DOWOLNY typ z ciągu, który można zdefiniować zarówno częściowo, jak i jako całość w zestawie, do którego istnieją odwołania zewnętrzne - a odwołania są dynamicznie dodawane przez użytkownika aplikacji).
Mam nadzieję, że to pomoże każdemu!
źródło
Podobnie jak w przypadku OP, musiałem załadować ograniczony podzbiór typów według nazwy (w moim przypadku wszystkie klasy były w jednym zestawie i zaimplementowano ten sam interfejs). Podczas próby użycia miałem wiele dziwnych problemów
Type.GetType(string)
przeciwko innym zestawom (nawet dodając AssemblyQualifiedName, jak wspomniano w innych postach). Oto jak rozwiązałem ten problem:Stosowanie:
var mytype = TypeConverter<ICommand>.FromString("CreateCustomer");
Kod:
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); } } } }
Oczywiście można zmodyfikować metodę CacheTypes, aby sprawdzić wszystkie zestawy w AppDomain lub inną logikę, która lepiej pasuje do Twojego przypadku użycia. Jeśli twój przypadek użycia pozwala na ładowanie typów z wielu przestrzeni nazw, możesz chcieć zmienić klucz słownika, aby
FullName
zamiast tego używał typu . Lub jeśli twoje typy nie dziedziczą ze wspólnego interfejsu lub klasy bazowej, możesz usunąć<BaseType>
i zmienić metodę CacheTypes, aby użyć czegoś takiego jak.GetTypes().Where(t => t.Namespace.StartsWith("MyProject.Domain.Model.")
źródło
Najpierw załaduj zespół, a następnie typ. ex: Assembly DLL = Assembly.LoadFile (PATH); DLL.GetType (typeName);
źródło
Czy możesz użyć jednego ze standardowych sposobów?
typeof( MyClass ); MyClass c = new MyClass(); c.GetType();
Jeśli nie, będziesz musiał dodać informacje do Type.GetType o zestawie.
źródło
Type.GetType(Type.GetType("MyProject.Domain.Model." + myClassName).AssemblyQualifiedName)
źródło