Udostępnij za pośrednictwem


Не давайте классам и пространствам имен одинаковые названия. Часть 1

В книге “Framework Design Guidelines” в разделе 3.4 сказано «не используйте одинаковые названия для пространства имен и типа внутри этого пространства имен». (*) То есть:

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

Что в этом плохого? О, позвольте мне перечислить все недостатки.

Часть 1. Коллизии между подключенными сборками.

Вы можете оказаться в ситуации, когда будете думать, что ссылаетесь на что-то одно, а на самом деле будете ссылаться на нечто другое. Предположим вы оказались в следующем положении: вы разрабатываете 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, и пока этого не выясним, даже не будем пытаться выяснить, на что ссылается второе Foo.

Эта ситуация открывает интересный момент в реализации алгоритма «привязки типов» языка C#. Т.е. того алгоритма, который определяет, о каком типе или пространстве имен идет речь в выражении “X.Y”. Мы не выполняем логического «возврата назад». Мы не рассуждаем следующим образом: «Пусть X означает вот это. Тогда Y не имеет смысла. Возвращаемся назад. Предположим X теперь означает вот то, ага, тогда Y имеет смысл». Мы вычисляем, что однозначно означает X, и только после этого выясняем, что означает Y. Если однозначно определить значение X невозможно, мы не проверяем все варианты для определения того, содержит ли какое-то из этих значений тип Y, мы просто сдаемся.

Предположим, что вы не можете изменить Foo.DLL, в этом случае остается либо удалить “using Foo”, и кто знает, к каким ошибкам это приведет, либо использовать extern alias:

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

Многие разработчики не знакомы с такой возможностью, как “extern alias”, поэтому многие из них, попавшие в эту ситуацию, не по собственной воле оказываются в безвыходном положении. Некоторый из них отправляют гневный письма людям, не являющихся источниками этой проблемы, например, мне.

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

****************

(*) FXCOP содержит флаги проверки более строгих рекомендаций. FXCOP сообщит о конфликте *любых* типов с *любым* пространством имен. Но сегодня я говорю только о простом сценарии, когда одноименный тип содержится в одноименном пространстве имен.

Оригинал статьи