2010-07-01 4 views
6

Rozważ następujący krótki fragment kodu.Czy możesz wyjaśnić ten przypadek krawędzi obejmujący słowo kluczowe C# 'using' z deklaracjami i członkami przestrzeni nazw?

namespace B 
{ 
    public class Foo 
    { 
     public string Text 
     { 
      get { return GetType().FullName; } 
     } 
    } 
} 

namespace A.B 
{ 
    public class Foo 
    { 
     public string Text 
     { 
      get { return GetType().FullName; } 
     } 
    } 
} 

Zapoznaj się z Przykład # 1 pierwszy.

using B; 

namespace A.C 
{ 
    public static class Program 
    { 
     public static void Main() 
     { 
      Console.WriteLine(new Foo().Text); 
     } 
    } 
} 

Rozważmy teraz Przykład # 2.

namespace A.C 
{ 
    using B; // Notice the placement here. 

    public static class Program 
    { 
     public static void Main() 
     { 
      Console.WriteLine(new Foo().Text); 
     } 
    } 
} 

Nie ma nic strasznie dziwnego w przykładzie nr 1. Jednak rzeczy stają się interesujące z przykładu nr 2. Koniecznie zwracaj szczególną uwagę na wszystkie identyfikatory użyte w przykładach. Jako zabawne ćwiczenie spróbuj odgadnąć, co się dzieje, bez podłączania tego do kompilatora. Nie ujawnię tutaj odpowiedzi, ponieważ 1) łatwo jest spróbować siebie i 2) Nie chcę zepsuć zabawy.

Czy program:

  • nie skompilować
  • wyświetlacz B.Foo
  • wyświetlacz ABFoo

pytanie ... Gdzie w specyfikacji C# jest to zachowanie opisane ?

Zerknąłem na sekcję 3.7 w specyfikacji C# 4.0, a zwłaszcza na punkt # 2, ale nie sądzę, że wyjaśnia to zachowanie. Jeśli już, to prawie sprawia, że ​​myślę, że kompilator zachowuje się sprzecznie ze specyfikacją.

+0

Powinieneś opublikować to pytanie autorowi tego bloga: http://blogs.msdn.com/b/ericlippert/ On będzie absolutnie mieć odpowiedź dla ciebie. – David

+0

Znam jego bloga. Często wysyła posty na SO, więc może zabrać tutaj. –

+0

Tak, sam oczekuję odpowiedzi Erica :) –

Odpowiedz

7

pierwszy przykład wydruki „B.Foo”, drugi przykład wypisuje „ABFoo”. Dzieje się tak dlatego, że w drugim przykładzie dyrektywa using B; jest zawarta w przestrzeni nazw A.C.

Dlaczego używa on A.B zamiast B?

Ponieważ wyszukiwania przestrzeni nazw podlegają tym samym zasadom, co wyszukiwania kwalifikacyjne nazw typu. Section 3.8 specyfikacji C#.

Zasadniczo, gdy kompilator przetwarza dyrektywę using, szuka się symbolu B w przestrzeni nazw A.C. Nie można go znaleźć, jest poszukiwany w przestrzeni nazw A.Ponieważ znajduje się tam podrzędny obszar nazw A, wybiera tę przestrzeń nazw i nie przechodzi do globalnej przestrzeni nazw, aby znaleźć przestrzeń nazw B.

Edit:
Jako @ P.Brian.Mackey sugeruje, można dostać się do przestrzeni nazw B z using global::B;.

+1

Tak, myślę, że masz rację. Odpowiedni fragment znajduje się w podpunktach nr 2 bullet # 1. Zdecydowanie, ale nie tak wyraźnie, opisuje to zachowanie zwrotne. Dobry chwyt. –

+0

@Brain: To ten, wraz z trzema kulami po. – Randolpho

5

Nie przeczytałem specyfikacji C#, ale mogę powiedzieć, co się dzieje po prostu przez odliczenie. Po umieszczeniu przy użyciu B w przestrzeni nazw A.C nie jesteś już w zasięgu globalnym, jesteś w zasięgu otaczającej przestrzeni nazw. Pierwsza aplikacja spróbuje rozwiązać w AC, następnie w A.

Najprostszy poprawka jest po prostu zmienić wnętrze stosując oświadczenie:

using global::B; 

Ale można dodatkowo zobaczyć ten zachodzącą poprzez dodanie

namespace A.C.B 
{ 
    public class Foo 
    { 
     public string Text 
     { 
      get { return GetType().FullName; } 
     } 
    } 
} 

pamiętać, że teraz rozwiązać do ACB

+0

+1, ale myślę, że masz kolejność, w której rozdzielczość występuje wstecz. Powinien najpierw spróbować rozwiązać w "A.C", a następnie "A". – Randolpho

+1

Dziękuję za poprawkę. Zostało zrobione. –

+0

Dobra wskazówka na temat sztuczki 'global :: B' przy okazji. –

0

Uważam, że istotne elementy specyfikacji są:

3.4.1 Członkowie przestrzeni nazw

Przestrzenie nazw i typów, które nie mają otaczającą przestrzeń nazw są członkami globalnej przestrzeni nazw. Odpowiada to bezpośrednio nazwom zadeklarowanym w deklaracji globalnej .

Przestrzenie nazw i typy zadeklarowane jako w przestrzeni nazw są członkami tej przestrzeni nazw . Odpowiada to bezpośrednio nazwom zadeklarowanym w przestrzeni deklaracji przestrzeni nazw.

Przestrzenie nazw nie mają dostępu do ograniczeń . Nie jest możliwe, aby zadeklarować prywatne, chronione lub wewnętrzne przestrzenie nazw, a nazwy przestrzeni nazw są zawsze publicznie dostępne.

Oprócz sekcji 9.4.2, w której omówiono, w jaki sposób stosowanie dyrektyw wpływa na zakres rozpoznawania identyfikatorów.

4

Pozostałe odpowiedzi są poprawne, ale jedna dodatkowa uwaga. Pomaga pamiętać, że

namespace A.C 
{ 
    using B; 

faktycznie jest tylko krótki sposób pisania

namespace A 
{ 
    namespace C 
    { 
     using B; 

które mogłyby sprawić, że nieco bardziej jasne, co się tu dzieje. Podczas rozwiązywania B możemy sprawdzić na B przed sprawdzamy pojemnik A.

Jeśli jesteś zainteresowany w sposób, który wyszukiwań przestrzeni nazw mogą iść strasznie źle, zobaczyć moją serię artykułów na ten temat:

http://blogs.msdn.com/b/ericlippert/archive/tags/namespaces/

+0

Ładny blog, dzięki. –

Powiązane problemy