'CompanyName. Нечто' является 'пространством имен', но используется как 'тип'

Повторное заявление вопроса

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

Если я имею...

public Foo MyFoo { get; set; }

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

Какова логика позади "пространства имен, используемого как тип" ошибка компилятора? От какой проблемы это сохраняет меня?

[И как я отмечаю Eric Lippert?:)]


Исходный вопрос


Проблема

У меня есть проект "Нечто" с пространством имен по умолчанию CompanyName.Foo. У меня есть база данных, это также называют "Нечто".

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

Затем когда я пытаюсь создать свойство с этим классом как тип, как это...

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

... Я получаю ошибку:

'CompanyName. Нечто' является 'пространством имен', но используется как 'тип'.

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

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

Вопросы:

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

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

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

Обновление

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

O.K.W. закрепил (2) и (3).

Использования

Запрос был выполнен для всех мой using операторы:

using System;
using System.ComponentModel;
using System.Data.Linq;
using System.Linq;
using MyCompany.Foo.Models;
45
задан devuxer 19 February 2010 в 19:03
поделиться

5 ответов

как мне пометить Эрика Липперта?

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

Я до сих пор совершенно не понимаю, почему компилятор 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 - это ABC.DEF». Следовательно, GHI - это ABC.DEF.GHI. Следовательно, JKL - это ABC.DEF.GHI.JKL, которого не существует, ошибка. Вы должны исправить ошибку, указав имя типа, которое позволяет компилятору определить, какой DEF вы имели в виду.

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

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

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

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

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

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

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

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

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

72
ответ дан 26 November 2019 в 21:11
поделиться

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

using CompanyName.Foo; 
... 
public Models.Foo DataContext { get; set; } 
1
ответ дан 26 November 2019 в 21:11
поделиться

C # Compiler не скомпилируется, когда существует неоднозначность между классом и пространством имен с тем же именем. К сожалению, вам просто нужно проверить пространство имен класса или переименовать базу данных. В вашем случае компилятор даже не добрался до конфликта, он умер после разрешения 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
        }
    }
}

, поэтому внутренний тест класса есть два неявных , используя :

using CompanyName;
using CompanyName.Foo;

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

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

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

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

    • Если имя пространства имен или типа появляется внутри тело класса или структуры Декларация, затем начиная с этого Декларация класса или структуры и продолжаясь с каждым приложенным классом или структурирование декларации (если есть), если Участник с указанным именем существует, доступны и обозначает тип, то Имя пространства имен или типа относится к этот член. Обратите внимание, что не типа Члены (константы, поля, методы, Свойства, индексаторы, операторы, Конструкторы экземпляра, деструкторы, и статические конструкторы) игнорируются При определении значения Название пространства имен или типа.

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

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

    • В противном случае, если пространство имен имеет соответствующий Декларация пространства имен включает Расположение Где то Имя пространства имен или типа происходит:

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

    • В противном случае, если пространства имен импортируются Использование-пространство-директивы Декларация пространства имен содержит ровно один тип с указанным именем, то Имя пространства имен или типа относится к этому тип.
      ...

(Bolding - это мой) Это означает, что при разрешении foo , сопоставление его против CompanyName.foo (первый смелый бит) происходит, прежде чем соответствовать его Используя директиву (вторая смелая сборка).

5
ответ дан 26 November 2019 в 21:11
поделиться
  1. Столкновение между пространством имен CompanyName.foo и CompanyName.foo.models.foo , а не foo . Я не совсем уверен, как / почему компилятор не может различать оба.

  2. Вы можете попробовать использовать алиас пространства имен для сокращения полной квалификации FOO
    E.G. Использование Coymodels = CompanyName.foo.models

  3. из ссылки , похоже, вы можете использовать / Context: <Тип> и / пространство имен: <имя > Укажите класс контекста данных (вместо использования имени таблицы) и пространства имен.

11
ответ дан 26 November 2019 в 21:11
поделиться

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

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

namespace Company.Foo
{
}

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

Наиболее близким к Foo является вложенное пространство имен 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 */
}

Править

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

0
ответ дан 26 November 2019 в 21:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: