как мне пометить Эрика Липперта?
Если у вас есть что-то, на что вы хотите обратить мое внимание, вы можете воспользоваться ссылкой "контакт" в моем блоге.
Я все еще совершенно сбит с толку, почему компилятор 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? YES . Были сделаны; мы определили значение DEF, это пространство имен. Мы узнаем значение DEF до и спрашиваем: «Есть ли у XYZ член DEF?»
Какие принципы дизайна влияют на этот дизайн?
Один принцип дизайна: «имена означают одно и то же независимо от того, как вы их используете». Язык не на 100% подчиняется этому принципу; Есть ситуации, в которых одно и то же имя может использоваться для ссылки на две разные вещи в одном и том же коде. Но в целом мы стремимся к ситуации, когда «Foo» два раза в одном и том же контексте означает одно и то же. (См. Мою статью о проблеме цветового цвета для получения более подробной информации по этому вопросу, а также мои статьи по , идентифицирующие нарушения правил "простого имени" .)
Один принцип разработки - «без возврата». В C # мы никогда не говорим: «Я вижу, что вы использовали имя для ссылки на что-то, на что нельзя ссылаться в этом контексте. Позвольте мне отказаться от результата привязки имени и начать заново, ища что-то, что могло бы работать».
Более общий принцип, лежащий в основе принципа «без возврата», заключается в том, что C # не является языком «угадай, что имел в виду пользователь». Вы написали программу, в которой наилучшее связывание идентификатора идентифицировало пространство имен, когда ожидался тип. Есть две возможности. Возможность первая: вы допустили ошибку, о которой хотите сообщить, чтобы you мог предпринять действия для ее исправления. Возможность вторая: вы имели в виду, что мы выбрали менее хорошее связывание, и поэтому мы должны угадать из всех возможных менее удачных связываний, чтобы выяснить, какое из них вы, вероятно, имели в виду.
Это хороший принцип проектирования в таких языках, как 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 { }
}
}
}
Разработка базового типа МНО. Без возврата мы говорим: «DEF - это ABC.DEF». Поэтому GHI - это ABC.DEF.GHI. Поэтому JKL - это ABC.DEF.GHI.JKL, которая не существует, ошибка. Вы должны исправить ошибку, указав имя типа, которое позволит компилятору определить, какой DEF вы имели в виду.
Если бы у нас был откат, что бы мы делали? Мы получаем эту ошибку, а затем возвращаемся. XYZ содержит DEF? Да. Содержит ли он GHI? Да. Содержит ли он JKL? Снова вернитесь назад. QRS содержит DEF.GHI.JKL? Да.
Что работает , но можем ли мы логически заключить из того, что оно работает, что это то, что пользователь имел в виду ?
Кто, черт возьми, знает в этой сумасшедшей ситуации? Мы получили все виды хороших привязок, которые потом очень плохо испортились в игре. Мысль о том, что мы натолкнулись на желаемый ответ, пройдя по многим тупикам, кажется весьма подозрительной.
Правильная вещь, которую нужно сделать здесь, это не возвращаться многократно и пробовать все виды худших привязок для каждой стадии поиска. Правильнее всего сказать: «приятель, наилучшее из возможных совпадений для этого поиска дает бессмысленные результаты; дайте мне что-то менее двусмысленное, чтобы работать здесь, пожалуйста».
Прискорбный факт написания языка, в котором компилятор по своему дизайну громко жалуется, если лучшее совпадение - это что-то, что не работает, - то, что разработчики часто говорят "ну, конечно, в общем Я хочу, чтобы компилятор указал на все мои ошибки - или, скорее, на все ошибки моего коллеги. Но для этого конкретного случая я знаю, что я делаю, поэтому, пожалуйста, компилятор, делать то, что я имею в виду, а не то, что я говорю . "
Проблема в том, что вы не можете иметь это в обоих направлениях. Вы не можете иметь оба компилятора, которые применяют жесткие правила, которые повышают вероятность того, что подозрительный код будет агрессивно идентифицирован как ошибочный и , разрешающий сумасшедший код с помощью эвристики компилятора, которая вычисляет «что я действительно имел в виду», когда вы пишете что-то, что компилятор совершенно правильно считает неоднозначным или неправильным.
Наглядный урок о том, как многим разработчикам сильно не нравятся эффекты языкового дизайна, который агрессивно идентифицирует ошибки, вместо того, чтобы догадываться, что разработчик имел в виду выбор худшего результата, см. 116 комментариев к этой статье по второстепенному и довольно незначительному аспекту разрешения перегрузки :
(Обратите внимание, что я больше не отвечаю на комментарии по этому вопросу; я объяснил свою позицию более десяти раз. Если все эти объяснения не убедительны, это потому, что я не очень хороший убедитель.)
И, наконец, если вы действительно хотите проверить свое понимание того, как работают правила разрешения имен в C #, попробуйте эту маленькую загадку . Почти все понимают неправильно или неправильно по неправильным причинам. Ответ здесь .