2010-09-21 12 views
17

Natknąłem się na dziwne zachowanie w moim (ogromnym) projekcie .NET 4. W pewnym momencie w kodzie, mam na myśli w pełni wykwalifikowanego typu, powiedzieć:Dlaczego System.Type.GetType ("Xyz") zwróci wartość null, jeśli istnieje typ (Xyz)?

System.Type type = typeof (Foo.Bar.Xyz); 

później, mogę to zrobić:

System.Type type = System.Type.GetType ("Foo.Bar.Xyz"); 

i wrócę null. Nie mogę zrozumieć, dlaczego tak się dzieje, ponieważ moje nazwisko jest poprawne, a sprawdziłem je z innymi typami i są one poprawnie rozwiązywane. Ponadto następujące kwerendy LINQ znajdzie typ:

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(); 

czy są jakieś powody, dla których System.Type.GetType może nie?

I w końcu musiał uciekać się do tego kawałka kodu zamiast 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; 
} 

Odpowiedz

26

Jeśli tylko podać nazwę klasy (który ma muszą być w pełni wykwalifikowani pod względem przestrzeni nazw, oczywiście) Type.GetType(string) będzie wyglądać tylko w aktualnie wykonującego montaż i mscorlib. Jeśli chcesz uzyskać typy z dowolnego innego zespołu, musisz podać pełną nazwę, w tym informacje o zespole. Jak mówi François, Type.AssemblyQualifiedName to dobry sposób na zobaczenie tego. Oto przykład:

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); 
    } 
} 

wyjściowa:

System.Windows.Forms.Form, System.Windows.Forms, Version = 4.0.0.0, Culture = neutral,
TokenKluczaPublicznego = b77a5c561934e089
System.Windows.Forms.Form

Zauważ, że jeśli używasz mocno nazwie zespołu (jak Form w tym przypadku) należy dołączyć wszystko informacja montaż - wersjonowanie, klucz publiczny żeton itp

Jeśli używasz non-silnie nazwie zespołu, łatwiej - coś takiego:

Foo.Bar.Baz, MyCompany.MyAssembly 

do typu zwanego Baz w przestrzeń nazw Foo.Bar, w złożeniu MyCompany.MyAssembly. Zwróć uwagę na brak ".dll" na końcu - jest to część nazwy pliku, ale nie nazwa zespołu.

Należy również pamiętać o różnicach między nazwami C# i nazwami CLR w przypadku klas zagnieżdżonych i generycznych. Na przykład typeof(List<>.Enumerator) ma nazwę System.Collections.Generic.List`1+Enumerator[T]. Strona generyczna jest trudna do wykonania, ale bit typu zagnieżdżonego jest łatwy - jest po prostu reprezentowany przez "+" zamiast "." używałbyś w języku C#.

+0

Dziękuję bardzo za odpowiedź. Rzeczywiście, wszystkie inne typy, które rozwiązywałem do tej pory, znajdowały się w tym samym zespole lub w mscorlib, więc wcześniej nie złapałem błędu. –

+0

Dostarczenie "System.Type.GetType" z częściowymi informacjami o montażu działa nawet wtedy, gdy złożenie ma silną nazwę. Sprawdziłem 'System.Type.GetType (" Foo.Bar.Baz, MyCompany.MyAssembly ")' i działa nawet jeśli 'MyCompany.Assembly' ma silną nazwę. –

+0

do wykorzystania w przyszłości, jeśli chcesz użyć odnośnika w podświetleniu kodu (a TY TYLKO będziesz;), użyj podwójnych odskoków, aby rozpocząć i zamknąć cytat :). Zobacz [tutaj] (http://meta.stackexchange.com/q/82718/237379). – Noctis

4

O ile wiem GetType szuka „xyz” w zespole o nazwie Foo.Bar.dll i ja zakładając, że nie istnieje.

Metoda GetType polega na podaniu dokładnej ścieżki do Xyz w złożeniu. Montaż i przestrzeń nazw nie muszą być powiązane.

Wypróbuj System.Type type = System.Type.GetType(typeof(Foo.Bar.Xyz).AssemblyQualifiedName) i sprawdź, czy to działa.

Powodem znalezienia go w przykładzie LINQ jest to, że używasz GetAssemblies, który uzyskuje złożenia, które zostały załadowane do bieżącego kontekstu wykonania, a zatem ma szczegóły potrzebne do znalezienia wszystkich typów w złożeniach.

4

Z MSDN documentation (podkreślenie moje):

Jeśli typeName obejmuje obszar nazw, ale nie nazwa zespołu, metoda ta wyszukuje tylko nazywając montażu obiektu i Mscorlib.dll, w tej kolejności. Jeśli nazwa_instancji jest w pełni kwalifikowana z częściową lub pełną nazwą zespołu, ta metoda wyszukuje w określonym zestawie. Jeśli złożenie ma silną nazwę, wymagana jest pełna nazwa zespołu.

1

Właśnie natknąłem się na podobny problem i chcą opuścić to tutaj

Przede wszystkim twój może określić AssemblyName w ciągu

var type = System.Type.GetType("Foo.Bar.Xyz, Assembly.Name"); 

Jednak to działa tylko dla zespołów bez silnego Nazwa. Objaśnienie jest już w Simons odpowiedź If the assembly has a strong name, a complete assembly name is required.

Mój problem polegał na tym, że musiałem rozwiązać System.Dictionary<?,?> z ciągu znaków w czasie wykonywania. Dla Dictionary<int, string> może to być łatwe, ale co z numerem Dictionary<int, Image>?

spowodowałoby

var typeName = "System.Collections.Generic.Dictionary`2[System.Int32, [System.Drawing.Image, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]]"; 

Ale nie chcę pisać silną nazwę. Zwłaszcza, że ​​nie chcę dołączać wersji, ponieważ zamierzam kierować wiele frameworków za pomocą mojego kodu.

Więc tu jest moje rozwiązanie

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(...) ma przeciążenia, które acceps FUNC do montażu i wpisać w rozwiązywaniu których schludny. Assembly.LoadWithPartialName jest przestarzałe, ale jeśli zostanie upuszczone w przyszłości, mogę wymyślić zastąpienie (powtórzenie wszystkich złożeń w bieżącym AppDomain i porównanie nazw częściowych).

Powiązane problemy