У меня есть метод, в котором мне нужно разрешить тип класса. Этот класс существует в другой сборке с пространством имен, аналогичным:
MyProject.Domain.Model
Я пытаюсь выполнить следующее:
Type.GetType("MyProject.Domain.Model." + myClassName);
Это отлично работает, если код, выполняющий это действие, находится в той же сборке, что и класс, тип которого я пытаюсь разрешить, однако, если мой класс находится в другой сборке, этот код не работает.
Я уверен, что есть гораздо лучший способ выполнить эту задачу, но у меня не было большого опыта в разрешении сборок и перемещении пространств имен внутри для определения типа класса, который я ищу. Какие-нибудь советы или подсказки, чтобы выполнить эту задачу более элегантно?
c#
.net
reflection
Брэндон
источник
источник
Ответы:
Вам нужно будет добавить имя сборки следующим образом:
Type.GetType("MyProject.Domain.Model." + myClassName + ", AssemblyName");
Чтобы избежать двусмысленности или если сборка находится в GAC, вы должны указать полное имя сборки, например:
Type.GetType("System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
источник
listType.MakeGenericType(itemType)
. Обе переменные типа могут быть созданы, используяType.GetType()
как в моем ответе.Это универсальное решение для людей , которым необходимо загрузить общие типы из динамических внешних ссылок по
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(); }
Я делюсь своим решением, чтобы помочь людям с той же проблемой, что и я (десериализовать ЛЮБОЙ тип из строки, которая может быть определена как частично, так и целиком во внешней сборке, и ссылки динамически добавляются пользователем приложения).
Надеюсь, это поможет кому-нибудь!
источник
Как и в случае с 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
вместо этого использовать тип . Или, если ваши типы не наследуются от общего интерфейса или базового класса, вы можете удалить<BaseType>
и изменить метод CacheTypes, чтобы использовать что-то вроде.GetTypes().Where(t => t.Namespace.StartsWith("MyProject.Domain.Model.")
источник
Сначала загрузите сборку, а затем тип. пример: Assembly DLL = Assembly.LoadFile (PATH); DLL.GetType (имя_типа);
источник
Можете ли вы использовать любой из стандартных способов?
typeof( MyClass ); MyClass c = new MyClass(); c.GetType();
Если нет, вам нужно будет добавить информацию о сборке в Type.GetType.
источник
Type.GetType(Type.GetType("MyProject.Domain.Model." + myClassName).AssemblyQualifiedName)
источник