Пространство имен и класс с тем же именем?

98

Я организую проект библиотеки, и у меня есть класс центрального менеджера с именем Scenegraphи целая куча других классов, которые живут в пространстве имен Scenegraph.

Мне бы очень хотелось, чтобы граф сцены MyLib.Scenegraphи другие классы были MyLib.Scenegraph.*, но, похоже, единственный способ сделать это - сделать все остальные классы внутренними классами Scenegraphв файле Scenegraph.cs, а это слишком громоздко. .

Вместо этого я организовал его как Mylib.Scenegraph.Scenegraphи MyLib.Scenegraph.*, что и как работает, но я обнаружил, что Visual Studio при некоторых условиях путается в том, имею ли я в виду класс или пространство имен.

Есть ли хороший способ организовать этот пакет так, чтобы он был удобен для пользователей, не сбивая весь мой код в неразбериху, которую невозможно поддерживать?

user430788
источник

Ответы:

111

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

В разделе 3.4 Руководства по дизайну фреймворка говорится: «Не используйте одно и то же имя для пространства имен и типа в этом пространстве имен». То есть:

namespace MyContainers.List 
{ 
    public class List { … } 
}

Почему это плохо? О, позволь мне посчитать пути.

Вы можете попасть в ситуации, когда думаете, что имеете в виду одно, а на самом деле имеете в виду другое. Предположим, вы попали в неприятную ситуацию: вы пишете Blah.DLL и импортируете Foo.DLL и Bar.DLL, которые, к сожалению, имеют тип Foo:

// Foo.DLL: 
namespace Foo { public class Foo { } }

// Bar.DLL: 
namespace Bar { public class Foo { } }

// Blah.DLL: 
namespace Blah  
{   
using Foo;   
using Bar;   
class C { Foo foo; } 
}

Компилятор выдает ошибку. «Foo» является двусмысленным между Foo.Foo и Bar.Foo. Облом. Думаю, я исправлю это, полностью указав имя:

   class C { Foo.Foo foo; } 

Теперь это дает ошибку двусмысленности « Foo в Foo.Foo неоднозначно между Foo.Foo и Bar.Foo ». Мы до сих пор не знаем, к чему относится первый Foo, и пока мы не сможем это выяснить, мы даже не пытаемся понять, к чему относится второй.

пинкерман
источник
8
Это интересный момент. Я все еще думаю об этом. Спасибо. Честно говоря, аргументы, которые начинаются со слов «Руководство по стилю», меня не впечатляют. Я видел очень много неоправданной чуши в руководствах по стилю. Но вы привели хороший практический аргумент выше. В любом случае, стоит подумать.
user430788
6
В ответе написано «чего не делать». А некоторые пользователи стека ищут «что делать». Кто-нибудь порекомендовал бы ответ Ant_222?
fantastory
3
Если вы действительно застряли, вы все равно можете создать псевдоним типа вне своего пространства имен. Внешние псевдонимы необходимы только тогда, когда у вас есть два полностью идентичных полных идентификатора (пространство имен + имя класса).
Джеффри
4
Все вышесказанное - до свидания и мнения. Дело в том , что вам не следует этого делать, потому что компилятор C # неправильно поймет это , несмотря на то, что ему достаточно четко сказано, что происходит. Например, using Foo.Bar;позже Bar bэто явно относится к Barклассу (или перечислению) внутри пространства имен Foo.Bar. Настоящая причина не делать этого заключается в том, что компилятор C # не может правильно его понять, что ужасно удивительно и прискорбно. Все остальное - совет нечеткого стиля.
TJ Crowder
2
Я не понимаю. Почему Foo.Foo неоднозначен? Вы прямо говорите компилятору, что хотите использовать класс Foo из пространства имен Foo в этом конкретном случае. Нет?
GuardianX
14

Присвоение того же имени пространству имен и классу может запутать компилятор, как говорили другие.

Как тогда это назвать?

Если пространство имен имеет несколько классов, найдите имя, которое определяет все эти классы.

Если пространство имен имеет только один класс (и отсюда искушение дать ему то же имя), назовите пространство имен ClassName NS . По крайней мере, так Microsoft называет свои пространства имен.

Перейти к
источник
5
У вас есть пример такого пространства имен от Microsoft?
sunefred 07
@sunefred Я искал его, но не нашел. Но я точно помню, что видел это в документации. Думаю, не так много случаев, когда вы хотите иметь один класс в пространстве имен.
GoTo
9

Я бы посоветовал вам последовать совету, который я получил, microsoft.public.dotnet.languages.csharpчтобы использовать MyLib.ScenegraphUtil.Scenegraphи MyLib.ScenegraphUtil.*.

Ant_222
источник
7

CA1724: Type Names Should Not Match Namespaces ...

По сути, если вы следуете анализу кода для правильного кодирования, это правило говорит, что не следует делать то, что вы пытаетесь сделать. Анализ кода очень полезен для поиска потенциальных проблем.

миерманский
источник
5

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

В моем случае, например, я не был человеком, принимающим такое решение, поэтому мне нужно было найти способ заставить его работать.

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

// Foo.DLL: 
namespace Foo { public class Foo { } }

// Bar.DLL: 
namespace Bar { public class Foo { } }

// Blah.DLL: 
namespace Blah
{
    using FooNSAlias = Foo;//alias
    using BarNSAlias = Bar;//alias
    class C { FooNSAlias.Foo foo; }//use alias to fully qualify class name
}

В основном я создавал «псевдонимы» пространства имен, что позволило мне полностью определить класс, и «путаница» Visual Studio исчезла.

ПРИМЕЧАНИЕ. Вам следует избегать этого конфликта имен, если это в ваших силах. Вы должны использовать упомянутую технику только тогда, когда вы не контролируете рассматриваемые классы и пространства имен.

Джонатан Альфаро
источник
2
Я проголосовал за него, потому что это было интересное решение несовершенного мира.
user430788
4

Просто добавляю свои 2 цента:

У меня был следующий класс:

namespace Foo {
    public struct Bar {
    }
    public class Foo {
        //no method or member named "Bar"
    }
}

Клиент был написан так:

using Foo;

public class Blah {
    public void GetFoo( out Foo.Bar[] barArray ) {
    }
}

Прощая ошибку, GetFoo не возвращает результат вместо использования параметра out, компилятор не смог разрешить тип данных Foo.Bar []. Он возвращал ошибку: не удалось найти тип или пространство имен Foo.Bar.

Похоже, что когда он пытается скомпилировать, он разрешает Foo как класс и не находит встроенный класс Bar в классе Foo. Также не удалось найти пространство имен Foo.Bar. Не удалось найти класс Bar в пространстве имен Foo. Точки в пространстве имен НЕ являются синтаксическими. Токеном является вся строка, а не слова, разделенные точками.

Такое поведение было продемонстрировано в VS 2015 с .Net 4.6.

Стив
источник
3

Как уже говорили другие, рекомендуется избегать именования класса так же, как его пространство имен.

Вот несколько дополнительных предложений по именованию из ответа svick на связанный вопрос «Тот же класс и имя пространства имен» в Software Engineering Stack Exchange:

Вы правы, что не следует называть пространство имен так же, как и тип, который он содержит. Я думаю, вы можете использовать несколько подходов:

  • Плюрализм: Model.DataSources.DataSource

Это особенно хорошо работает, если основная цель пространства имен - содержать типы, унаследованные от одного и того же базового типа или реализующие один и тот же интерфейс.

  • Сократить: Model.QueryStorage

Если пространство имен содержит только небольшое количество типов, возможно, вам вообще не нужно это пространство имен.

  • Сделайте предприятие: Model.ProjectSystem.Project

Это может работать особенно с функциями, которые являются важной частью вашего продукта, поэтому они заслуживают собственного названия.

(Обратите внимание , что ответ выше использование Model.DataSource.DataSource, Model.QueryStorage.QueryStorage.иModel.Project.Project в качестве примеров , а неMyLib.Scenegraph.Scenegraph .)

(Я также нашел полезными другие предложения по именованию в других ответах здесь.)

сынок
источник
2

Старый пост, но здесь я иду с другой идеей, которая может кому-то помочь:

«... но кажется, что единственный способ сделать это - сделать все остальные классы внутренними классами Scenegraph в файле Scenegraph.cs, а это слишком громоздко».

Это действительно лучшая реализация для множества сценариев. Но я согласен, что наличие всего этого кода в одном файле .cs раздражает (мягко говоря).

Вы можете решить эту проблему, сделав базовый класс «частичным классом», а затем продолжив создание внутренних классов в их собственных файлах (просто помните, что им нужно будет объявить дополнение базового класса, а затем перейти к конкретному внутреннему классу для этого файла).

Что-то типа...

Scenegraph.cs:

namespace MyLib
{
    public partial class Scenegraph
    {
        //Scenegraph specific implementations
    }
}

DependentClass.cs:

namespace MyLib
{
    public partial class Scenegraph
    {
        public class DependentClass
        {
            //DependentClass specific implementations
        }
    }
}

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

Марсело Мьяра
источник
1

Это происходит, когда это основной класс пространства имен. Таким образом, это одна из мотиваций - поместить пространство имен в библиотеку, и проблема исчезнет, ​​если вы добавите «Lib» к имени пространства имен ...

namespace SocketLib
{
    class Socket
    {
Пол Сампнер
источник