Подтвердить что ты не робот

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

Повторение вопроса

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

Если у меня есть...

public Foo MyFoo { get; set; }

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

Какова логика ошибки компилятора, используемого как тип type? Какая проблема заключается в том, чтобы спасти меня?

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


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


Проблема

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

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

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

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

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

'CompanyName.Foo' - это "пространство имен", но используется как "тип".

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

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

Вопросы:

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

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

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

Обновление

Ищем ответ на (1).

O.K.W. (2) и (3).

Usings

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

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

Ответ 1

как я могу пометить Eric Lippert?

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

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

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

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

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" в одном и том же контексте, это означает одно и то же. (См. Мою статью о Проблема цветового цвета для некоторых подробностей об этом, а также мои статьи о выявление нарушений правил "простого имени" .)

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

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

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

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

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 комментариев к этой статье о незначительном и довольно несущественном аспекте разрешения перегрузки:

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

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

Ответ 2

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

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

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

Ответ 3

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

Итак, внутри класса Test есть два неявных using s:

using CompanyName;
using CompanyName.Foo;

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

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

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

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

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

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

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

    • В противном случае, если пространство имен имеет соответствующее Объявление пространства имен, включающее где namespace-или-type-name, затем:

    • Если объявление пространства имен содержит using-alias-directive, который связывает данное имя с импортированным пространства имен или типа, тогда namespace-type-name ссылается на это пространство имен или тип.

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

Поскольку вы использовали точечную нотацию для разделения Company и Foo, вы неявно создаете пространство имен компаний с вложенным пространством имен 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 */
}

Изменить

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