问题描述
问题的重述
我复活这个问题,因为我只是碰到了这个错误再次今天,我依然心乱如麻,为什么C#编译器困扰,以检查上下文它是没有意义的命名空间的存在空间和类型之间的冲突。
如果我有...
公共富MyFoo {搞定;组; }
...为什么会编译器小心,富
既是一个命名空间和类型?你可以声明一个属性为命名空间,而不是一个类型?
什么是背后的编译器错误一样使用类型命名空间逻辑?哪些问题是这救了我的?
[?我如何标记埃里克利珀? :)]
原始的问题
问题
我有一个项目富与默认命名空间 CompanyName.Foo
。我有一个数据库,它也被称为富。
当我在数据库上运行SqlMetal.exe,它会生成一个类 CompanyName.Foo .Models.Foo
。
然后,当我试图创建一个属性与此类的类型,像这样...
使用CompanyName.Foo.Models;
...
公共美孚的DataContext {搞定;组; }
...我得到的错误:
I am forced to do...
public CompanyName.Foo.Models.Foo Foo { get; set; } // :-(
Questions:
Why does this error occur? My property declaration doesn't contain
CompanyName
, so why is this a problem? Simply put:Foo != CompanyName.Foo
. Also, just to be sure, I did a search of my entire solution fornamespace Foo
and came up with zero hits (if I had actually used a namespaceFoo
, I could understand getting an error).[answered] Is there any way around fully qualifying
Foo
every time I want to use it?[answered] Is there any way to get SqlMetal to name the class anything other than
Foo
(w/o changing the name of my database)? I can change the namespace using a switch, but I don't know of a way to change the actual class name.
Update
Still seeking an answer to (1).
O.K.W. nailed (2) & (3).
Usings
A request was made for all my using
statements:
using System;
using System.ComponentModel;
using System.Data.Linq;
using System.Linq;
using MyCompany.Foo.Models;
If you have something you want brought to my attention you can use the "contact" link on my blog.
Indeed, the rules here are tricky. Coincidentally, two weeks ago I wrote and posted a series of blog articles about some of these issues closely related to this very issue; they'll actually go live in early March. Watch the blog for details.
UPDATE: The articles mentioned above are here:
http://blogs.msdn.com/b/ericlippert/archive/tags/namespaces/
Let me rephrase the question into several questions.
I think that's already been covered satisfactorily in other answers. The type resolution algorithm is extremely well-specified. But just to sum up: being inside something of the right name "binds more tightly" than using something of the right name from the outside. When you say:
using XYZ;
namespace ABC.DEF
{
class GHI : DEF { }
}
that is the same as
using XYZ;
namespace ABC
{
namespace DEF
{
class GHI : DEF { }
}
}
So now we must determine the meaning of DEF. We go from inside to outside. Is there a type parameter of GHI called DEF? No. Look at the container. Is there a member of DEF called DEF? No. Look at the container. Is there a member of ABC called DEF? YES. We're done; we have determined the meaning of DEF, it is a namespace. We discover the meaning of DEF before we ask "does XYZ have a member DEF?"
One design principle is "names mean the same thing no matter how you use them". The language does not 100% obey this principle; there are situations in which the same name can be used to refer to two different things in the same code. But in general, we strive for a situation where when you see "Foo" two times in the same context, it means the same thing. (See my article on The Color Color Problem for some details on this, as well as my articles on identifying violations of the "simple name" rules.)
One design principle is "no backtracking". We do not ever say in C# "I see that you used a name to refer to something that is not legal to refer to in this context. Let me abandon the result of name binding and start over, looking for something that might work."
A larger principle that underlies the "no backtracking" principle is that C# is not a "guess what the user meant" language. You wrote a program where the best possible binding of an identifier identified a namespace when a type was expected. There are two possibilities. Possibility one: you've made an error that you want to be told about so that you can take action to correct it. Possibility two: you meant for a less-good binding to be the one we choose, and so we should guess from amongst all the possible less-good bindings to figure out which one you probably meant.
That's a good design principle in languages like JScript -- JScript is all about muddling on through when the developer does something crazy. C# is not that kind of language; the feedback we get loud and clear from our developers is tell me when something is broken so I can fix it.
The thing about "no backtracking" is that it makes the language much easier to understand. Suppose you have something like this mess:
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 { }
}
}
}
Work out the base type of MNO. With no backtracking we say "DEF is ABC.DEF". Therefore GHI is ABC.DEF.GHI. Therefore JKL is ABC.DEF.GHI.JKL, which does not exist, error. You must fix the error by giving a type name that lets the compiler identify which DEF you meant.
If we had backtracking, what would we have to do? We get that error, and then we backtrack. Does XYZ contain a DEF? Yes. Does it contain a GHI? Yes. Does it contain a JKL? No. Backtrack again. Does QRS contain an DEF.GHI.JKL? Yes.
That works, but can we logically conclude from the fact that it works that it is the one the user meant?
Who the heck knows in this crazy siutation? We got all kinds of good bindings in there that then went bad very late in the game. The idea that we stumbled upon the desired answer after going down many blind alleys seems highly suspect.
The correct thing to do here is not to backtrack multiple times and try out all kinds of worse bindings for every stage of the lookup. The correct thing to do is to say "buddy, the best possible match for this lookup gives nonsensical results; give me something less ambiguous to work with here please."
An unfortunate fact about writing a language where the compiler by design complains loudly if the best match is something that doesn't work, is that developers frequently say "well, sure, in general I want the compiler to point out all my mistakes -- or, rather, all my coworker's mistakes. But for this specific case, I know what I am doing, so please, compiler, do what I mean, not what I say."
Trouble is, you can't have it both ways. You can't have both a compiler that both enforces rigid rules that make it highly likely that suspicious code will be aggressively identified as erroneous and allow crazy code via compiler heuristics that figure out "what I really meant" when you write something that the compiler quite rightly sees as ambiguous or wrong.
For an object lesson in how lots of pro devs vehemently dislike the effects of a language design that aggressively identifies errors rather than guessing that the developer meant for the worse result to be chosen, see the 116 comments to this article on a minor and rather unimportant aspect of overload resolution:
(Note that I am no longer responding to comments on this issue; I've explained my position over ten times. If all those explanations are not convincing, that's because I'm not a very good convincer.)
And finally, if you really want to test your understanding of how the name resolution rules work in C#, try out this little puzzle. Almost everyone gets it wrong, or gets it right for the wrong reasons. The answer is here.
这篇关于“CompanyName.Foo'是'空间',但用于像一个”类型“的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!