'CompanyName.Foo'是一个'命名空间',但是像'types'

重述问题

我正在复活这个问题,因为我今天再次遇到了这个错误,而且我仍然完全困惑,为什么C#编译器会在命名空间不存在的情况下检查命名空间和types之间的冲突。

如果我有…

public Foo MyFoo { get; set; } 

…为什么编译器会关心Foo是一个名称空间还是一个types? 你可以声明一个属性作为一个命名空间,而不是一个types?

“类似”编译器错误使用的命名空间背后的逻辑是什么? 这是什么问题拯救了我?

[我如何标记Eric Lippert? :)]


原来的问题


问题

我有一个默认命名空间CompanyName.Foo项目“Foo”。 我有一个也被称为“Foo”的数据库。

当我在数据库上运行SqlMetal.exe时,它会生成一个类CompanyName.Foo.Models.Foo

然后,当我尝试创build一个属性与这个类作为types,像这样…

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

…我得到的错误:

'CompanyName.Foo'是一个'名字空间',但是像'type'一样使用。

我被迫做…

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

问题:

  1. 为什么会发生这种错误? 我的财产声明不包含CompanyName ,那么为什么这是一个问题? 简单地说: Foo != CompanyName.Foo 。 另外,为了确保,我search了我的namespace Foo整个解决scheme,并得出了零个命中(如果我实际上使用了命名空间Foo ,我可以理解得到一个错误)。

  2. 每当我想使用它时,有没有什么办法可以完全符合Foo呢?

  3. [回答]有没有什么办法让SqlMetal命名除Foo以外的其他类(不改变我的数据库的名称)? 我可以使用开关来更改名称空间,但我不知道更改实际类名的方法。

更新

仍然在寻求答案(1)。

OKW钉(2)&(3)。

Usings

我的所有using陈述都提出了要求:

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

我如何标记Eric Lippert?

如果你有什么想要引起我的注意,你可以使用我的博客上的“联系”链接。

我仍然完全困惑,为什么C#编译器会在命名空间不存在的情况下检查名称空间和types之间的冲突。

事实上,这里的规则是棘手的。 巧合的是,两周前我写了一系列有关这个问题的一些关于这个问题的博客文章, 他们实际上将在三月初上线。 观看博客的细节。

更新:上面提到的文章在这里:

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

为什么会发生这种错误?

让我把这个问题改为几个问题。

规范的哪些部分certificate了这个错误的产生?

我认为在其他答案中已经得到了令人满意的回答。 typesparsingalgorithm是非常明确的。 但是总结一下:在正确的名字里面 ,比从外面 使用正确的名字更“紧密地结合”。 当你说:

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

那是一样的

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

所以现在我们必须确定DEF的含义。 我们从内到外。 是否有称为DEF的GHI的types参数? 不,看容器。 DEF有DEF吗? 不,看容器。 有没有一个名为DEF的ABC成员? 是的 。 我们完成了 我们已经确定了DEF的含义,它是一个命名空间。 我们问“XYZ是否有成员DEF?” 之前,我们发现了DEF的含义。

什么devise原则影响这个devise?

一个devise原则是“无论你如何使用它们,名字都是一样的”。 语言不是100%服从这个原则; 在某些情况下,可以使用相同的名称来指代同一代码中的两个不同的事物。 但总的来说,我们争取在同一个环境中两次看到“Foo”的情况下,它意味着同样的事情。 (有关此颜色的一些细节,以及关于识别违反“简单名称”规则的文章,请参阅关于“颜色颜色问题 ”的文章。)

一个devise原则是“不回溯”。 我们从来没有用C#语言说:“我看到你用了一个名字来指代在这个上下文中不合法的东西,让我放弃名字绑定的结果,重新开始,寻找可能有用的东西。

“不回溯”原则的一个更大的原则是C#不是“猜测用户的意思”的语言。 你写了一个程序,当一个types被预期的时候,一个标识符的最好的绑定标识了一个名字空间。 有两种可能性。 可能性一:你犯了一个你想要被告知的错误,以便可以采取行动来纠正它。 可能性二:你意味着一个不太好的约束是我们select的约束,所以我们应该从所有可能的不太好的约束中猜出你可能指的是哪一个。

在JScript这样的语言中,这是一个很好的devise原则 – 当开发人员做了一些疯狂的事情时,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的基本types。 没有回溯,我们说“DEF是ABC.DEF”。 所以GHI是ABC.DEF.GHI。 因此JKL是ABC.DEF.GHI.JKL,不存在,错误。 你必须通过给出一个types名称来修正错误,让编译器识别你的意思。

如果我们回溯,我们该怎么办? 我们得到这个错误,然后我们回溯。 XYZ是否包含DEF? 是。 它是否包含GHI? 是。 它是否包含JKL? 不要再回去 QRS包含DEF.GHI.JKL吗? 是。

这是有效的 ,但是我们能从逻辑上推断出它是用户所指的那个事实吗?

这个疯子呢,谁知道呢? 我们在那里得到了各种各样的好装备,然后在比赛中很晚很糟糕。 我们在经历了许多盲目的小巷之后,偶然发现了所期望的答案,这种想法似乎非常值得怀疑。

这里要做的正确的事情不是多次回溯,而是在查找的每个阶段尝试各种更糟糕的绑定。 正确的做法是说:“哥们,这种查找的最好匹配给出了无意义的结果;请给我一些不太模糊的东西,请在这里工作。

关于编写一种语言的不幸事实是,编译器如果大声抱怨,如果最好的匹配是不起作用的话,那么开发者经常会说:“好吧,当然,我希望编译器指出我所有的错误 -或者说是我同事的所有错误,但是对于这个具体情况,我知道我在做什么,所以请编译, 我的意思是说,不是我说的

麻烦的是,你不能两面都有。 你不能同时拥有一个编译器,这两个编译器都强制执行严格的规则,使得很可能将可疑的代码严重地标识为错误, 并且通过编译器启发式方法允许疯狂的代码,当编写一些编译器时会发现“我的意思”很正确地认为是模棱两可或错误的。

对于许多专业开发人员强烈反对一种语言devise的效果的实际教训,这种语言devise积极地识别错误,而不是猜测开发者意味着要select更坏的结果,请参阅本文中有关次要而不重要的116条评论重载分辨率方面 :

(注意,我不再回应这个问题的评论,我已经解释了我的位置十次以上,如果所有的解释都不能说服,那是因为我不是一个很好的说服者。)

最后,如果你真的想testing你对C#中名称parsing规则的理解, 试试这个小小的谜题 。 几乎每个人都错了,或者因为错误的原因而正确地做对了。 答案就在这里 。

  1. 冲突在命名空间CompanyName.FooCompanyName.Foo.Models.Foo之间,而不是Foo 。 我不确定如何/为什么编译器不能区分两者。

  2. 您可以尝试使用名称空间别名来缩短完全限定的Foo
    例如using coyModels = CompanyName.Foo.Models

  3. 从引用看来,您可以使用/context:<type>/namespace:<name>来指定数据上下文类(而不是使用表名)和名称空间。

当类和名称空间之间存在歧义时,C#编译器不能编译。 不幸的是,你只需要明确命名空间类或重命名数据库。 在你的情况下,编译器甚至没有得到冲突,它将Fooparsing为命名空间后死亡。

每当你有这样的事情:

 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被parsing为命名空间,因此错误。

编辑好点。 我已经从MSDN中挖掘出来了:

名称空间或types名称的含义如下确定:

  • 如果名称空间或types名称由单个标识符组成:

    • 如果名称空间或types名称出现在类或结构声明的主体中,则从该类或结构声明开始,继续每个包含类或结构声明(如果有的话),如果具有给定名称的成员存在,是可访问的,并且表示一个types,那么命名空间或types名称是指那个成员。 请注意,在确定名称空间或types名称的含义时,会忽略非types成员(常量,字段,方法,属性,索引器,运算符,实例构造函数,析构函数和静态构造函数)。

    • 否则,从名称空间或types名称出现的名称空间开始,继续每个封闭名称空间(如果有的话),并以全局名称空间结束,将评估以下步骤,直到find实体:

    • 如果名称空间包含具有给定名称的名称空间成员 ,则namespace-or-type-name指向该成员,并根据成员将其分类为名称空间或types。

    • 否则,如果名称空间有一个相应的名称空间声明,它包含名称空间或types名称出现的位置,则:

    • 如果名称空间声明包含将给定名称与导入的名称空间或types关联的using-alias-directive ,则名称空间或types名称引用该名称空间或types。

    • 否则,如果由命名空间声明的using-namespace-directive导入的名称空间恰好包含具有给定名称的一个types,则namespace-or-type-name引用该types。

(Bolding是我的)这意味着在parsingFoo ,将它与CompanyName.Foo (第一个粗体位)匹配,然后将它与using指令(第二个粗体构build)匹配。

你为什么不能这样做?

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

当我在一个单独的类库中引用一个类时,popup了这个问题,其types与名称空间的根名称相同。 最初,当在单独的控制台应用程序项目中引用此types时,没有任何问题,所有内容都可以正常编译。 然而,从Windows服务项目引用产生的is a 'namespace' but is used like a 'type'. 信息。 原来Windows服务项目的目标框架设置为“.NET Framework 4客户端configuration文件”。 将其更改为“.NET Framework 4”消除了错误。 希望这可以帮助有类似情况的人。

我是新来的C#和我开始接触到这个错误反编译应用程序,保存为一个项目,试图立即重新编译…为什么应用程序能够编译在第一个地方超出了我的..但是。 ..问题和解决scheme非常简单: 默认情况下,在添加一个新的类时,c#为名称空间使用与命名空间中类相同的名称!!!!! 这是不好的,因为没有一些隐藏的标识符明确地告诉您所指的是哪个(名称空间或types),编译器无法区分这个区别! 卫生署! 去c#的方式! …解决scheme:不要重新命名一千件事情,而是仔细检查所有更正,运行项目,当你有错误列表在你面前时,依次点击每个问题。 一旦在“foo”处,在“foo”之后键入一个点(。),使其显示:foo。 这应该调出包含在其中的类的菜单。 在这个列表中,双击“foo”(或重新input名称)将原来的“foo”更改为“foo.foo”…对每个错误和问题都解决! 瞧!!!! 我对复杂名称的整个应用程序做了这个,它运作得很好! 快乐的编码! – Tim H.

由于您使用点符号来区分公司和Foo,因此您隐式创build了一个公司名称空间,并带有嵌套的Foo名称空间,而不是您所信任的Company.Foo。

这就是为什么这不起作用:

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

Foo最接近的是公司命名空间中嵌套的Foo命名空间。 但是,您可以这样做:

 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 */ } 

编辑

伊戈尔说同样的事情,但更技术性。

如果您有一个名称空间和一个具有相同名称的类,则会生成unit testing。 你不应该像Eric Lippert在这里解释的那样:

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