«CompanyName.Foo» – это «пространство имен», но используется как «тип»,

Пересмотр вопроса

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

Если бы у меня был…

public Foo MyFoo { get; set; } 

… почему компилятор заботится о том, что Foo является и пространством имен, и типом? Можете ли вы объявить свойство как пространство имен вместо типа?

Какова логика compiler errors, используемого как тип типа? С какой проблемой это спасает меня?

[И как я могу пометить Эрика Липперта? :)]


Оригинальный вопрос


Проблема

У меня есть проект «Foo» с пространством имен по умолчанию CompanyName.Foo . У меня есть firebase database, которая также называется «Foo».

И когда я запускаю SqlMetal.exe в базе данных, он генерирует class CompanyName.Foo.Models.Foo .

Затем, когда я пытаюсь создать свойство с этим classом в качестве типа, например …

 using CompanyName.Foo.Models; ... public Foo DataContext { get; set; } 

… Я получаю сообщение об ошибке:

«CompanyName.Foo» – это «пространство имен», но используется как «тип».

Я вынужден сделать …

 public CompanyName.Foo.Models.Foo Foo { get; set; } // :-( 

Вопросов:

  1. Почему эта ошибка возникает? Объявление моей собственности не содержит CompanyName , так почему же это проблема? Проще говоря: Foo != CompanyName.Foo . Кроме того, чтобы быть уверенным, я выполнил поиск всего моего решения для namespace Foo и придумал нулевые удары (если бы я фактически использовал пространство имен Foo , я мог бы понять, получив ошибку).

  2. [ответил] Есть ли какой-либо способ полностью определить Foo каждый раз, когда я хочу его использовать?

  3. [ответил] Есть ли способ заставить SqlMetal назвать class чем-то другим, кроме Foo (без изменения имени моей базы данных)? Я могу изменить пространство имен с помощью коммутатора, но я не знаю, как изменить фактическое имя classа.

Обновить

Все еще ищу ответ на (1).

OKW гвоздь (2) и (3).

Usings

Была сделана просьба обо всех моих заявлениях:

 using System; using System.ComponentModel; using System.Data.Linq; using System.Linq; using MyCompany.Foo.Models; 

как я могу пометить Эрика Липперта?

Если у вас есть что-то, что вы хотите привлечь к себе, вы можете использовать ссылку «контакт» в своем блоге.

Я все еще совершенно смущен, почему компилятор C # пытается проверить наличие конфликтов между пространствами имен и типами в контекстах, где нет смысла, чтобы пространство имен существовало.

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

ОБНОВЛЕНИЕ: статьи, упомянутые выше, приведены здесь:

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

Почему эта ошибка возникает?

Позвольте мне перефразировать вопрос на несколько вопросов.

Какие разделы спецификации оправдывают создание этой ошибки?

Я думаю, что это уже было удовлетворительно рассмотрено в других ответах. Алгоритм разрешения по типу очень хорошо определен. Но просто подытожим: быть внутри чего-то правильного имени «привязывается более тесно», чем использовать что-то из правильного имени извне . Когда ты говоришь:

 using XYZ; namespace ABC.DEF { class GHI : DEF { } } 

это то же самое, что и

 using XYZ; namespace ABC { namespace DEF { class GHI : DEF { } } } 

Итак, теперь мы должны определить значение DEF. Мы идем изнутри наружу. Есть ли параметр типа GHI, называемый DEF? Нет. Посмотрите на контейнер. Есть ли член DEF, называемый DEF? Нет. Посмотрите на контейнер. Есть ли член ABC, называемый DEF? ДА . Были сделаны; мы определили значение DEF, это пространство имен. Мы обнаруживаем значение DEF, прежде чем мы спросим: «У XYZ есть член DEF?»

Какие принципы дизайна влияют на этот дизайн?

Один принцип проектирования – «имена означают одно и то же, независимо от того, как вы их используете». Язык не выполняет 100% этого принципа; есть ситуации, когда одно и то же имя может использоваться для обозначения двух разных вещей в одном и том же коде. Но в целом мы стремимся к ситуации, когда два раза вы видите «Foo» в одном и том же контексте, это означает одно и то же. (См. Мою статью о проблеме цвета цвета для некоторых подробностей об этом, а также мои статьи об идентификации нарушений правил «простого имени» .)

Один принцип проектирования – «нет возврата». Мы никогда не говорим в C # «Я вижу, что вы использовали имя, чтобы ссылаться на то, что неправомочно ссылаться в этом контексте. Позвольте мне отказаться от результата привязки имени и начать сначала, ища что-то, что может работать».

Более крупный принцип, лежащий в основе принципа «нет возврата», заключается в том, что C # не означает «предполагать, что означает пользователь». Вы написали программу, в которой наилучшее возможное связывание идентификатора идентифицировало пространство имен при ожидании типа. Есть две возможности. Возможность одна: вы сделали ошибку, о которой хотите рассказать, чтобы вы могли принять меры для ее исправления. Возможность две: вы имели в виду менее подходящую привязку, чтобы быть той, которую мы выбираем, и поэтому мы должны угадать из всех возможных менее хороших привязок, чтобы выяснить, какой из них вы, вероятно, имели в виду.

Это хороший принцип дизайна на таких языках, как JScript – JScript – все о запутывании, когда разработчик делает что-то безумное. C # – не такой язык; обратная связь, которую мы получаем громко и ясно от наших разработчиков, говорит мне, когда что-то сломано, поэтому я могу это исправить .

Дело в том, что «нет обратного отслеживания» заключается в том, что язык значительно упрощает понимание. Предположим, у вас есть что-то вроде этого беспорядка:

 namespace XYZ.DEF { public class GHI {} } namespace QRS.DEF.GHI { public class JKL { } } ... using QRS; namespace TUV { using XYZ; namespace ABC { namespace DEF { class GHI { } class MNO : DEF.GHI.JKL { } } } } 

Разработайте базовый тип MNO. Без возврата мы говорим «DEF is ABC.DEF». Поэтому GHI – ABC.DEF.GHI. Поэтому JKL – это ABC.DEF.GHI.JKL, которого нет, ошибка. Вы должны исправить ошибку, указав имя типа, которое позволяет компилятору определить, какой DEF вы имели в виду.

Если бы у нас было отступление, что бы мы сделали? Мы получаем эту ошибку, а затем возвращаемся обратно. Имеет ли XYZ DEF? Да. Имеет ли он GHI? Да. Он содержит JKL? Нет. Возврат назад. Имеет ли QRS DEF.GHI.JKL? Да.

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

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

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

Несчастный факт о написании языка, где компилятор по дизайну жалуется громко, если наилучшее совпадение – это то, что не работает, заключается в том, что разработчики часто говорят «хорошо, конечно, в общем, я хочу, чтобы компилятор указывал на все мои ошибки – или, вернее, все ошибки моего коллеги. Но для этого конкретного случая я знаю, что делаю, поэтому, пожалуйста, компилятор, сделайте то , что я имею в виду, а не то, что я говорю ».

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

Для объектного урока о том, как много профессионалов явно не любят эффекты языкового дизайна, которые агрессивно идентифицируют ошибки, а не предполагают, что разработчик предназначен для того, чтобы худший результат был выбран, см. 116 комментариев к этой статье о незначительном и довольно несущественном аспект разрешения перегрузки :

(Обратите внимание, что я больше не отвечаю на комментарии по этому вопросу, я объяснил свою позицию более десяти раз. Если все эти объяснения не убедительны, это потому, что я не очень хороший собеседник.)

И, наконец, если вы действительно хотите проверить свое понимание того, как правила разрешения имен работают на C #, попробуйте эту маленькую загадку . Почти все ошибаются, или исправляются по неправильным причинам. Ответ здесь .

  1. Столкновение между пространствами имен CompanyName.Foo и CompanyName.Foo.Models.Foo , а не Foo . Я не совсем уверен, как / почему компилятор не может отличить оба.

  2. Вы можете попробовать использовать псевдоним пространства имен, чтобы сократить полный квалификационный Foo
    например, using coyModels = CompanyName.Foo.Models

  3. Из ссылки кажется, что вы можете использовать /context: и /namespace: чтобы указать class контекста данных (вместо имени таблицы) и пространства имен.

Компилятор C # не компилируется, когда существует неоднозначность между classом и пространством имен с тем же именем. К сожалению, вам просто нужно явно указать пространство classа или переименовать базу данных. В вашем случае компилятор даже не попал в конфликт, он умер после разрешения Foo как пространства имен.

Всякий раз, когда у вас есть что-то вроде этого:

 using CompanyName.Foo.Models; namespace CompanyName.Foo { class Test { public Foo Model { get; set; } // error CS0118: 'CompanyName.Foo' is a 'namespace' but is used like a 'type' public Foo1 Model { get; set; } //OK } } namespace CompanyName.Foo.Models { class Foo1 { } class Foo { } } 

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

 namespace CompanyName { using CompanyName; //<--using1 - Implicit using, since we should be able to access anything within CompanyName implicitly. namespace Foo { using CompanyName.Foo; //<-- using2 Same as above class Test { public Foo Model { get; set; } //At this stage due to using1 Foo is actually CompanyName.Foo, hence the compiler error } } } 

Таким образом, внутри classа Test есть два неявных using s:

 using CompanyName; using CompanyName.Foo; 

Следовательно, Foo разрешен к пространству имен, следовательно, к ошибке.

EDIT Хорошая точка. Я выкопал это из MSDN :

Значение имени пространства имен или имен определяется следующим образом:

  • Если пространство имен или имя-типа состоит из одного идентификатора:

    • Если имя пространства имен или пространства имен появляется внутри тела объявления classа или структуры, то, начиная с этого объявления classа или структуры и продолжая с каждым объявляющим объявлением classа или структуры (если есть), если существует член с указанным именем , доступен и обозначает тип, тогда имя пространства имен или имен имен относится к этому члену. Обратите внимание, что при определении значения имени пространства имен или имен типа игнорируются элементы не-типа (константы, поля, методы, свойства, индексы, операторы, конструкторы экземпляров, деструкторы и статические конструкторы).

    • В противном случае, начиная с пространства имен, в котором происходит имя пространства имен или типа, продолжается с каждым охватывающим пространством имен (если есть) и заканчивается глобальным пространством имен, следующие шаги оцениваются до тех пор, пока объект не будет расположен:

    • Если пространство имен содержит элемент пространства имен с заданным именем , то имя-пространство-тип-имя ссылается на этот элемент и, в зависимости от члена, classифицируется как пространство имен или тип.

    • В противном случае, если пространство имен имеет соответствующее объявление пространства имен, содержащее место, где имеет место имя или тип-имя, тогда:

    • Если декларация пространства имен содержит директиву using-alias, которая связывает данное имя с импортированным пространством имен или типом, имя пространства имен или типа имен относится к этому пространству имен или типу.

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

(Bolding is mine) Это означает, что при разрешении Foo сопоставление его с CompanyName.Foo (первый жирный бит) происходит до его сопоставления using директивой using (вторая жирная строчка).

почему ты не можешь просто сделать

 using CompanyName.Foo; ... public Models.Foo DataContext { get; set; } 

У меня возникла эта проблема, когда я ссылался на class в отдельной библиотеке classов, где его тип имел то же имя, что и корень пространства имен. Первоначально, ссылаясь на этот тип в отдельном проекте приложения консоли, проблем не было, все скомпилировано в порядке. Однако ссылка из проекта службы Windows is a 'namespace' but is used like a 'type'. сообщение. Оказывается, что в Windows Service Project установлена ​​целевая платформа «.NET Framework 4 Client Profile». Изменение этого параметра на «.NET Framework 4» устранило ошибку. Надеюсь, это поможет кому-то в подобной ситуации.

Я новичок в c #, и я вступил в контакт с этой ошибкой при декомпиляции приложения ac #, сохраняя в качестве проекта, попытку немедленно перекомпилировать … почему приложение было в состоянии скомпилировать, в первую очередь, вне меня. .. проблема и решение довольно просто: по умолчанию при добавлении нового classа c # использует то же имя для пространства имен, как и для classа в пространстве имен !!!!! Это плохо, потому что без какого-либо скрытого идентификатора, явно указывающего, к какому (пространству имен или типу) вы обращаетесь, компилятор не может сказать разницу !!!!! DOH! способ пойти c # !!!! … РЕШЕНИЕ: вместо того, чтобы переименовать тысячу вещей и дважды проверить все исправления, запустите проект, когда у вас есть список ошибок перед вами, щелкните по очереди, чтобы перейти к каждой проблеме. Однажды в вопросе «foo» введите точку (.) После упомянутого «foo», чтобы она отображалась: foo. .. это должно вызвать меню classов, содержащихся внутри. В этом списке дважды щелкните «foo» (или просто введите имя), изменив исходное «foo» на «foo.foo» … Сделайте это для каждой ошибки и проблемы! Вуаля !!!! Я сделал это для целого приложения со сложными именами, и он отлично поработал! Счастливое кодирование! – Тим Х.

Поскольку вы использовали точечную нотацию для разделения Company и Foo, вы неявно создаете пространство имен компаний с вложенным пространством имен Foo, а не Company.Foo, как вы считаете.

Вот почему это не работает:

 namespace Company.Foo { } namespace Company.Foo.Models { public class TestClass { public Foo DataContext { get; set; } } } 

Самое близкое к Foo – это nested пространство имен Foo в пространстве имен Company. Однако вы можете это сделать:

 using Company.Foo; using Company.Foo.Models; namespace Company.Foo { class Program { public static void Main() {} } } namespace Company.Foo.Models { public class Foo { } } public class DataContextClass { public Foo DataContext { get; set; } /* Foo here is automatically resolved to Company.Foo.Models.Foo */ } 

редактировать

Игорь сказал то же самое, но был более техническим.

Это также происходит, если вы создаете модульные тесты, когда у вас есть пространство имен и class с тем же именем. Что вы никогда не должны делать, как объяснил Эрик Липперт здесь:

http://blogs.msdn.com/b/ericlippert/archive/2010/03/09/do-not-name-a-class-the-same-as-its-namespace-part-one.aspx

  • Преимущество перехода на оператор if-else
  • Почему C требуется ключевое слово struct, а не C ++?
  • Haskell GHC: какова временная сложность совпадения шаблона с конструкторами N?
  • Неожиданный порядок оценки (ошибка компилятора?)
  • Interesting Posts

    В чем разница между веб-методом asp.net и службой wcf?

    Как инвертировать цветовую схему веб-страниц в Firefox?

    Как может примитивное значение float быть -0.0? Что это значит?

    Перенос слов для ярлыка в Windows Forms

    Как мне поменять свое аудиоустройство?

    jQuery: как мне оживить поворот div?

    Как я могу настроить таргетинг иерархии каталогов с помощью программных ссылок в Linux?

    Как выполнить Java-программу с C #?

    Получить имя таблицы базы данных из Entity Framework MetaData

    Какая худшая ошибка в C # или .NET?

    Самый простой способ шифрования текстового файла в java

    Какие специальные символы должны быть экранированы в регулярных выражениях?

    Как использовать «Статические методы фабрики» вместо конструкторов?

    Как я могу правильно передать уникальные дополнения к ожидающемуся намерению?

    Docker – способ предоставить доступ к USB-порту или последовательному устройству?

    Давайте будем гением компьютера.