Почему System.Type.GetType( "Xyz" ) возвращает null, если существует типof (Xyz)?
Я столкнулся с странным поведением в моем (огромном) проекте .NET 4. В какой-то момент кода я имею в виду полностью квалифицированный тип, скажем:
System.Type type = typeof (Foo.Bar.Xyz);
позже, я делаю это:
System.Type type = System.Type.GetType ("Foo.Bar.Xyz");
и я вернусь null
. Я не могу понять, почему это происходит, потому что мое имя типа правильно, и я проверил с другими типами, и они правильно решены. Более того, следующий запрос LINQ находит тип:
var types = from assembly in System.AppDomain.CurrentDomain.GetAssemblies ()
from assemblyType in assembly.GetTypes ()
where assemblyType.FullName == typeName
select assemblyType;
System.Type type = types.FirstOrDefault ();
Есть ли причины, по которым System.Type.GetType
может выйти из строя?
Мне, наконец, пришлось прибегнуть к этой части кода вместо GetType
:
System.Type MyGetType(string typeName)
{
System.Type type = System.Type.GetType (typeName);
if (type == null)
{
var types = from assembly in System.AppDomain.CurrentDomain.GetAssemblies ()
from assemblyType in assembly.GetTypes ()
where assemblyType.FullName == typeName
select assemblyType;
type = types.FirstOrDefault ();
}
return type;
}
Ответы
Ответ 1
Если вы просто укажете имя класса (которое, конечно же, должно быть полностью определено с точки зрения пространства имен) Type.GetType(string)
будет отображаться только в текущей исполняемой сборке и mscorlib. Если вы хотите получать типы из любой другой сборки, вам нужно указать полное полное имя, включая информацию о сборке. Как говорит Франсуа, Type.AssemblyQualifiedName
- хороший способ увидеть это. Вот пример:
using System;
using System.Windows.Forms;
class Test
{
static void Main()
{
string name = typeof(Form).AssemblyQualifiedName;
Console.WriteLine(name);
Type type = Type.GetType(name);
Console.WriteLine(type);
}
}
Вывод:
System.Windows.Forms.Form, System.Windows.Forms, Version = 4.0.0.0, Culture = neutral,
PublicKeyToken = b77a5c561934e089
System.Windows.Forms.Form
Обратите внимание, что если вы используете сильно названную сборку (например, Form
в этом случае), вы должны включить всю информацию о сборке - управление версиями, токен открытого ключа и т.д.
Если вы используете сборку с ненавязчивым именем, это проще - что-то вроде:
Foo.Bar.Baz, MyCompany.MyAssembly
для типа, называемого Baz
в пространстве имен Foo.Bar
, в сборке MyCompany.MyAssembly
. Обратите внимание на отсутствие ".dll" в конце - эту часть имени файла, но не имя сборки.
Вы также должны знать различия между именами С# и именами CLR для таких вещей, как вложенные классы и дженерики. Например, typeof(List<>.Enumerator)
имеет имя System.Collections.Generic.List`1+Enumerator[T]
. Сторона дженериков сложна для разработки, но бит вложенного типа прост - он просто представлен как "+", а не ".". вы должны использовать в С#.
Ответ 2
Насколько я знаю, GetType ищет "Xyz" в сборке с именем Foo.Bar.dll, и я предполагаю, что ее не существует.
GetType полагается на передачу точного пути к Xyz в сборке.
Сборка и пространство имен не обязательно должны быть связаны.
Попробуйте System.Type type = System.Type.GetType(typeof(Foo.Bar.Xyz).AssemblyQualifiedName)
и посмотрите, работает ли это.
Причина, по которой вы нашли это в примере LINQ, заключается в том, что вы используете GetAssemblies
, который получает сборки, которые были загружены в текущий контекст выполнения, и, следовательно, содержит детали, необходимые для поиска всех типов в сборках.
Ответ 3
Из документация MSDN (мой акцент):
Если typeName включает пространство имен, но не имя сборки, этот метод ищет только сборку вызывающего объекта и Mscorlib.dll в этом порядке. Если typeName имеет полную квалификацию с неполным или полным именем сборки, этот метод выполняет поиск в указанной сборке. Если сборка имеет сильное имя, требуется полное имя сборки.
Ответ 4
Я просто наткнулся на подобную проблему и хочу оставить это здесь.
Прежде всего, вы можете указать AssemblyName в строке
var type = System.Type.GetType("Foo.Bar.Xyz, Assembly.Name");
Однако это работает только для сборок без сильного имени. Объяснение уже в ответе Саймонса If the assembly has a strong name, a complete assembly name is required.
Моя проблема заключалась в том, что я должен был разрешить System.Dictionary<?,?>
из строки во время выполнения. Для Dictionary<int, string>
это может быть легко, но как насчет Dictionary<int, Image>
?
это приведет к
var typeName = "System.Collections.Generic.Dictionary`2[System.Int32, [System.Drawing.Image, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]]";
Но я не хочу писать сильное имя. Особенно потому, что я не хочу включать версии, так как планирую нацелить несколько фреймворков на свой код.
Итак, вот мое решение
privat statice void Main()
{
var typeName = "System.Collections.Generic.Dictionary`2[System.Int32, [System.Drawing.Image, System.Drawing]]";
var type = Type.GetType(typeName, ResolveAssembly, ResolveType);
}
private static Assembly ResolveAssembly(AssemblyName assemblyName)
{
if (assemblyName.Name.Equals(assemblyName.FullName))
return Assembly.LoadWithPartialName(assemblyName.Name);
return Assembly.Load(assemblyName);
}
private static Type ResolveType(Assembly assembly, string typeName, bool ignoreCase)
{
return assembly != null
? assembly.GetType(typeName, false, ignoreCase)
: Type.GetType(typeName, false, ignoreCase);
}
Type.GetType(...)
имеет перегрузку, которая активирует функцию для сборки и разрешения типа, которая в аккуратном состоянии. Assembly.LoadWithPartialName
устарел, но если он упадет в будущем, я мог бы подумать о замене (повторить все сборки в текущем AppDomain и сравнить частичные имена).