Простір імен і клас з однаковим ім’ям?


95

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

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

Натомість я організував це як Mylib.Scenegraph.Scenegraphі MyLib.Scenegraph.*, який вид робіт працює, але я виявляю, що Visual Studio заплутується за певних умов щодо того, чи я маю на увазі клас або простір імен.

Чи є хороший спосіб організувати цей пакет, щоб він був зручним для користувачів, не стискаючи весь мій код разом у неможливому безладі?

Відповіді:


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 in Foo.Foo неоднозначний між Foo.Foo і Bar.Foo ". Ми досі не знаємо, до чого відноситься перший Фу, і поки ми не можемо це зрозуміти, ми навіть не намагаємося з’ясувати, до чого відноситься другий.


6
Це цікавий момент. Я все ще розмовляю над цим. Дякую. Я повинен бути чесним, аргументи, які починаються з "Керівництво стилем говорить", не вражають мене. Я бачив надзвичайно багато невиправданих лайнів у стилістичних посібниках. Але ви наводите хороший практичний аргумент вище. У будь-якому випадку, варто подумати.
user430788

5
У відповіді сказано "що не можна робити". Хоча деякі користувачі стеку шукають "що робити". Хтось порекомендував би Ant_222 відповідь?
фантасторія

3
Якщо ви дійсно застрягли, ви все ще можете створити псевдонім типу поза вашим простором імен. Зовнішні псевдоніми потрібні лише тоді, коли у вас є два повністю однакові повні ідентифікатори (простір імен + назва класу).
Джеффрі

4
Все вищесказане - це прощання та думка. Справа в тому , що ви не повинні цього робити, тому що компілятор C # це неправильно зрозуміє , незважаючи на те, що йому досить чітко сказано, що відбувається. Наприклад, using Foo.Bar;потім пізніше Bar bцілком чітко йдеться Barпро клас (або перелік) всередині простору імен Foo.Bar. Справжня причина не робити цього полягає лише в тому, що компілятор C # не може правильно зрозуміти це, що надзвичайно дивно і прикро. Все інше - це вовняна порада в стилі.
TJ Crowder

2
Я не розумію. Чому Foo.Foo неоднозначний? Ви безпосередньо говорите компілятору, що хочете використовувати клас Foo із простору імен Foo у цьому конкретному випадку. Немає?
GuardianX

14

Надання того самого імені простору імен і класу може заплутати компілятор, як сказали інші.

Як тоді його назвати?

Якщо у просторі імен є кілька класів, знайдіть ім'я, яке визначає всі ці класи.

Якщо у просторі імен є лише один клас (і, отже, спокуса дати йому те саме ім'я), назвіть простір імен ClassName NS . Так Microsoft принаймні називає їхні простори імен.


4
Чи є у вас приклад такого простору імен від Microsoft?
сунефред

Я мушу його пошукати і повернутися до вас.
GoTo

@sunefred Я шукав його, але не міг знайти. Але я точно пам’ятаю, що бачив це в документації. Думаю, не так багато випадків, коли ви хочете мати один клас у просторі імен.
GoTo

9

Я пропоную вам дотримуватися порад, я отримав щодо microsoft.public.dotnet.languages.csharpвикористання MyLib.ScenegraphUtil.Scenegraphі MyLib.ScenegraphUtil.*.


7

CA1724: Type Names Should Not Match Namespaces ...

В основному, якщо ви дотримуєтесь аналізу коду для правильного кодування, це правило говорить не робити те, що ви намагаєтеся зробити. Аналіз коду дуже корисний, щоб допомогти вам знайти потенційні проблеми.


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 як клас і не знайшов вбудовану панель класу в класі Foo. Також не вдалося знайти простір імен під назвою Foo.Bar. Не вдалося шукати панель класів у просторі імен Foo. Крапки в просторі імен НЕ є синтаксичними. Весь рядок - це лексема, а не слова, розділені крапками.

Цю поведінку демонстрував VS 2015 під управлінням .Net 4.6


4

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

Наприклад, у моєму випадку я не був людиною, яка приймала таке рішення, тому мені потрібно було знайти спосіб змусити це працювати.

Отже, для тих, хто не може змінити ні назву простору імен, ні назву класу, це спосіб, за допомогою якого ви можете змусити свій код працювати.

// 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

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

Я думаю, що це тим ближче, що ти можеш наблизитись до чистої реалізації внутрішніх класів, не маючи на тому, щоб захаращувати все усередині одного величезного і брудного файлу.


2

Як вже говорили інші, корисно уникати іменувати клас так само, як і його простір імен.

Ось кілька додаткових пропозицій щодо присвоєння імен із відповіді svick на відповідне запитання "Той самий клас і ім'я простору імен" в Exchange Engineering Stack Exchange:

Ви маєте рацію, що не слід називати простір імен однаковим із типом, який він містить. Думаю, можна використати кілька підходів:

  • Плюралізація: Model.DataSources.DataSource

Це особливо добре працює, якщо основною метою простору імен є містити типи, які успадковуються від одного базового типу або реалізують один і той же інтерфейс.

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

Якщо простір імен містить лише невелику кількість типів, можливо, цей простір імен вам зовсім не потрібен.

  • Зробити підприємницьким: Model.ProjectSystem.Project

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

(Зверніть увагу , що наведені вище відповіді використовуються Model.DataSource.DataSource, Model.QueryStorage.QueryStorage.і в Model.Project.Projectякості прикладів , а не MyLib.Scenegraph.Scenegraph.)

(Я також знайшов корисними інші пропозиції щодо найменування в інших відповідях тут.)


1

Це трапляється, коли це основний клас простору імен. Отже, це одна з мотивацій помістити простір імен у бібліотеку, тоді проблема зникає, якщо до імені простору імен додати 'Lib' ...

namespace SocketLib
{
    class Socket
    {
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.