Вложенные пространства имен

#c#

#c#

Вопрос:

У меня есть что-то вроде этого:

 namespace n1
{
    namespace n2
    {
        class foo{}
    }
}
  

В другом файле я пишу:

 using n1;
  

Почему я не могу сейчас напечатать что-то вроде:

 n2.foo smthing;
  

И как сделать что-то подобное возможным?

Ответ №1:

Это преднамеренное правило C #. Если вы сделаете это:

 namespace Frobozz
{
    namespace Magic
    {
        class Lamp {}
    }

    class Foo
    {
        Magic.Lamp myLamp; // Legal; Magic means Frobozz.Magic when inside Frobozz
    }
}
  

Это законно. Но это не:

 namespace Frobozz
{
    namespace Magic
    {
        class Lamp {}
    }
}

namespace Flathead
{
    using Frobozz;
    class Bar
    {
        Magic.Lamp myLamp; // Illegal; merely using Frobozz does not bring Magic into scope
    }
}
  

Правило C #, описывающее это, содержится в разделе 7.6.2 спецификации C # 4. Это очень запутанный раздел; вам нужен абзац ближе к концу, в котором говорится

В противном случае, если пространства имен, импортированные директивами using-namespace- объявления пространства имен, содержат ровно один тип с именем I…

Ключевым моментом является то, что в нем говорится «ровно один тип», а не «точно один тип или пространство имен«. Мы намеренно запрещаем вам «нарезать» имя пространства имен подобным образом, когда вы находитесь за пределами этого пространства имен, потому что это потенциально сбивает с толку. Как говорили другие, если вы хотите сделать что-то подобное, полностью укажите это один раз в директиве using-alias, а затем используйте псевдоним.

Комментарии:

1. Здесь довольно поздно, но… Могу ли я использовать эту область пространства имен для ОПРЕДЕЛЕНИЯ вложенных пространств имен без необходимости использовать полные имена для новых пространств имен? Это значительно упростило бы задачу, избежав дублирования текста и опечаток…

2. @heltonbiker: Звучит как отличный вопрос для StackOverflow … на котором вы уже находитесь. Опубликуйте вопрос!

3. Что ж, вы правы. Интересно, что я попробовал это, и это работает. К сожалению, пространства имен отображаются на панели просмотра классов Visual Studio только в том случае, если внутри них что-то определено (например, класс). Спасибо за ваше внимание!

4. Почему этот ответ не принимается? Принятое, похоже, не имеет отношения к тому, что задается в этом разделе.

5. @wEight: принятый ответ выбирается пользователем, который задал вопрос. Я знаю, это может показаться странным, но часто люди задают вопросы, которые лишь косвенно связаны с проблемой, которая у них действительно есть.

Ответ №2:

Используйте псевдонимы пространств имен:

 using n2 = n1.n2;

...
n2.foo something;
  

Перед именем класса должно быть полное пространство имен (с / или другими именами классов для вложенных типов). Усеченное пространство имен работать не будет.

Ответ №3:

По замыслу, namespaces они предназначены для того, чтобы помочь вам определить область действия.

Если вы не уточните это полностью, вы получите ошибку, которую видите.

Предполагая, что File1 имеет что-то вроде этого:

 namespace n1
{
    namespace n2
    {
        class Foo { }
    }
}
  

Вы можете сделать это двумя способами:

Полностью квалифицированный using

Содержимое File2:

 namespace n3
{
    using n1.n2;

    class TestClass
    {
        private Foo something;
    }
}
  

Используйте namespace alias

 namespace n3
{
    using n2 = n1.n2;

    class TestClass
    {
        private n2.Foo something;
    }
}
  

Ответ №4:

Параграф 4 раздела 9.4.2 спецификации языка C # явно объясняет это поведение:

Директива using-namespace- импортирует типы, содержащиеся в данном пространстве имен, но конкретно не импортирует вложенные пространства имен.

Далее даже приводится пример, который очень похож на ваш собственный.

 namespace N1.N2
{
    class A {}
}
namespace N3
{
    using N1;
    class B: N2.A {}        // Error, N2 unknown
}
  

Конечно, если бы вы сделали это:

 namespace n1
{
  public class Example
  {
    public static void Main()
    {
      n2.Foo a; // This is legal.
    }
  }
}
  

Это было бы скомпилировано, потому что n2 доступно, поскольку на него ссылаются из блока пространства имен предка.

Комментарии:

1. В чем причина этого правила?

2. @Ini Если подумать, то разрешение такого каскадирования пространств имен нарушило бы одну из основных целей использования пространств имен, которая заключается в предотвращении конфликтов имен.

Ответ №5:

Вы не можете написать это, так как n2 находится внутри n1 . Если вы хотите получить доступ к n2 пространству имен, вы можете попробовать ввести using n2 = n1.n2 в начале вашего другого файла.

Ответ №6:

Один из способов сделать это — объявить ваше вложенное пространство имен как класс.

Вы можете иметь:

 namespace NS1
{
    public class NNS1
    {
        public class C { }
    }
}
namespace NS2
{
    public class NNS2
    {
        public class C { }
    }
}
  

Затем используйте свои пространства имен с помощью:

 using NS1;
using NS2;

class C
{
    public C()
    {
        var c1 = new NNS1.C();
        var c2 = new NNS2.C();
    }
}
  

Однако это не может создать объединение пространств имен. Если у вас есть:

 namespace NS1
{
    public class NNS
    {
        public class C1 { }
    }
}
namespace NS2
{
    public class NNS
    {
        public class C2 { }
    }
}
  

И

 using NS1;
using NS2;

class C
{
    public C()
    {
        var c1 = new NNS.C1();
        var c2 = new NNS.C2();
    }
}
  

Компилятор будет жаловаться на NNS двусмысленность, поскольку в этом контексте он может ссылаться на два символа класса. Я попытался объявить пространства имен как интерфейсы и использовать класс для их реализации для создания объединения пространств имен. Однако реализация интерфейса не приводит к автоматическому импорту в него символов вложенных классов.

Я не знаю, почему C # не поддерживает ссылку на вложенное пространство имен, и я понятия не имею о недостатках объявления пространств имен как классов. Пожалуйста, прокомментируйте, если кто-нибудь знает.