web-dev-qa-db-ja.com

「CompanyName.Foo」は「名前空間」ですが、「タイプ」のように使用されます

質問の修正

私は今日このエラーに再び遭遇したので、この質問を復活させています。そして、C#コンパイラが名前空間が存在する意味がないコンテキストで名前空間と型の間の衝突をチェックするのをなぜわざわざ混乱させています。

私が持っている場合...

public Foo MyFoo { get; set; }

...なぜFooが名前空間と型の両方であるとコンパイラが気にするのですか?プロパティを型ではなく名前空間として宣言できますか?

「型のように使用される名前空間」コンパイラエラーの背後にあるロジックは何ですか?これが私を救う問題は何ですか?

[そしてEric Lippertにタグを付けるにはどうすればよいですか? :)]


元の質問


問題

デフォルトの名前空間CompanyName.Fooを持つプロジェクト "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; } // :-(

質問:

  1. なぜこのエラーが発生するのですか?プロパティ宣言にCompanyNameが含まれていないのに、なぜこれが問題なのですか?簡単に言えば:Foo != CompanyName.Foo。また、念のため、namespace Fooのソリューション全体を検索し、ヒットがゼロになった(実際に名前空間Fooを使用した場合、エラーが発生することを理解できました)。

  2. [回答済み]Fooを使用するたびに完全修飾する方法はありますか?

  3. [回答済み]SqlMetalにFoo以外のクラスの名前を付ける方法はありますか(私の名前を変更せずに)データベース)?スイッチを使用して名前空間を変更できますが、実際のクラス名を変更する方法がわかりません。

更新

まだ(1)への答えを探しています。

O.K.W.釘付け(2)&(3)。

使用法

すべてのusingステートメントに対して要求が行われました。

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

eric Lippertにタグを付けるにはどうすればよいですか?

私の注意を喚起したいものがある場合は、私のブログの「連絡先」リンクを使用できます。

名前空間が存在する意味がないコンテキストで、C#コンパイラが名前空間と型の間の衝突をわざわざチェックする理由は、まだ完全に混乱しています。

確かに、ここでのルールは注意が必要です。偶然にも、2週間前、私はこの問題に密接に関連するこれらの問題のいくつかに関する一連のブログ記事を書いて投稿しました。実際に3月上旬に公開されます。詳細についてはブログをご覧ください。

更新:上記の記事は次のとおりです。

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

なぜこのエラーが発生するのですか?

質問をいくつかの質問に言い換えてみましょう。

このエラーの発生を正当化する仕様のセクションはどれですか?

それはすでに他の回答で十分にカバーされていると思います。型解決アルゴリズムは非常に詳細に指定されています。ただまとめると:inside正しい名前の何かがusing正しい名前の何かよりoutside。あなたが言う時:

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

それは同じです

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

そのため、DEFの意味を判断する必要があります。私たちは内側から外側へ行きます。 DEFというGHIの型パラメーターはありますか?いいえ。コンテナを見てください。 DEFと呼ばれるDEFのメンバーはありますか?いいえ。コンテナを見てください。 DEFというABCのメンバーはいますか? [〜#〜] yes [〜#〜]。終わったね; DEFの意味を決定しました。これは名前空間です。 DEFの意味を発見するbefore「XYZにはメンバーDEFがいますか?」

この設計に影響を与える設計原則は何ですか?

設計原則の1つは、「名前はどのように使用しても同じことを意味する」です。言語はこの原則を100%守っていません。同じ名前を使用して、同じコード内の2つの異なるものを参照できる場合があります。しかし、一般的に、同じコンテキストで「Foo」を2回見ると、同じことを意味する状況を目指して努力しています。 (詳細については The Color Color Problem の私の記事と 「単純名」ルールの違反を特定する の私の記事を参照してください。)

1つの設計原則は、「バックトラッキングなし」です。 C#で、「このコンテキストで参照するのに適していないものを参照するために名前を使用したことがわかりました。名前バインディングの結果を放棄して、動作する可能性のあるものを探してみましょう。」

「バックトラッキングなし」の原則の根底にあるより大きな原則は、C#は「ユーザーの意図を推測する」言語ではないということです。あなたは、タイプが期待されるときに識別子の可能な限り最良のバインディングが名前空間を識別するプログラムを書きました。 2つの可能性があります。可能性1:youがそれを修正するためのアクションを取ることができるように、あなたがあなたに伝えたいエラーを犯しました。可能性2:あなたは私たちが選択するものがあまり良いバインディングではないことを意味しているので、あなたはどの良いものを見つけるためにすべての可能性の低い悪いバインディングの中からguessする必要がありますおそらく意味。

これは、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 is ABC.DEF」と言います。したがって、GHIはABC.DEF.GHIです。したがって、JKLはABC.DEF.GHI.JKLであり、存在しません、エラー。コンパイラーが意図したDEFを識別できるタイプ名を指定して、エラーを修正する必要があります。

バックトラックがある場合、何をしなければなりませんか?このエラーが発生した後、バックトラックします。 XYZにはDEFが含まれていますか?はい。 GHIが含まれていますか?はい。 JKLが含まれていますか?いいえ。バックトラック。 QRSにはDEF.GHI.JKLが含まれていますか?はい。

それはworksですが、それが動作するという事実から、それがユーザーmeantであると論理的に結論付けることができますか?

この狂気の中で誰が知っているのか?そこにはあらゆる種類の優れたバインディングがあり、それはゲームの非常に遅い段階で悪化しました。多くの盲目の路地を下った後、私たちが希望する答えを見つけたという考えは非常に疑わしいようです。

ここで行う正しいことは、何回もバックトラックし、ルックアップのすべての段階であらゆる種類のより悪いバインディングを試さないことです。正しいことは、「バディ、このルックアップに最適な一致は無意味な結果をもたらします。ここで作業するのに曖昧さの少ないものをください」と言うことです。

コンパイラーが設計上最適な一致が機能しないものである場合に大声で文句を言う言語を記述することに関する不幸な事実は、開発者が頻繁に「まあ、確かに、generalコンパイラにすべての間違いを指摘してほしい-むしろ、同僚のすべての間違いを指摘してほしい。しかし、このspecificの場合、私は何をしているのか知っているので、コンパイラ、私が言っていることではなく、私が言っていることをしてください

問題は、両方の方法でそれを持つことはできません。疑わしいコードが積極的にエラーとして識別される可能性が非常に高くなる厳格なルールを強制するコンパイラと、を発見するコンパイラヒューリスティックによるクレイジーコードの両方を許可することはできません。コンパイラが曖昧または間違っていると非常に正しく見ているものを書くとき、.

開発者が悪い結果を選択することを意図していると推測するのではなく、積極的にエラーを識別する言語設計の効果を多くのプロ開発者が激しく嫌う方法に関するオブジェクトレッスンについては、 この記事に対する116のコメントオーバーロード解決のマイナーでかなり重要でない側面

(この問題についてのコメントにはもはや回答していないことに注意してください。自分の立場を10回以上説明しました。これらすべての説明が納得できない場合、それは私があまりよくないからです。)

そして最後に、C#で名前解決規則がどのように機能するかについての理解を本当にテストしたい場合は、 この小さなパズルを試してみてください 。ほとんどの人が間違っているか、間違った理由で正しいと思っています。答えは here です。

72
Eric Lippert
  1. 衝突は名前空間CompanyName.FooおよびCompanyName.Foo.Models.Fooであり、Fooではありません。しかし、コンパイラが両方を区別できない理由/理由を正確に知りません。

  2. namespace alias を使用して、完全修飾Fooを短縮できます。
    例えば。 using coyModels = CompanyName.Foo.Models

  3. 参照 から、/context:<type>および/namespace:<name>(テーブル名を使用する代わりに)データコンテキストクラスと名前空間を指定します。

11
o.k.w

C#コンパイラは、クラスと同じ名前の名前空間との間にあいまいさが存在する場合、コンパイルしません。残念ながら、クラスの名前を明示的に指定するか、データベースの名前を変更する必要があります。あなたの場合、コンパイラは競合にさえ到達せず、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には、2つの暗黙的なusingsがあります。

using CompanyName;
using CompanyName.Foo;

したがって、Fooはネームスペースに解決されるため、エラーになります。

[〜#〜] edit [〜#〜]良い点。これを [〜#〜] msdn [〜#〜] から掘り下げました。

名前空間またはタイプ名の意味は、次のように決定されます。

  • Namespace-or-type-nameが単一の識別子で構成される場合:

    • Namespace-or-type-nameがクラスまたは構造体の宣言の本体内にある場合、そのクラスまたは構造体の宣言から開始し、与えられた名前のメンバーが存在する場合は、クラスまたは構造体の宣言(存在する場合)を続けます、アクセス可能であり、型を示す場合、名前空間または型名はそのメンバーを参照します。名前空間またはタイプ名の意味を決定するとき、非型メンバー(定数、フィールド、メソッド、プロパティ、インデクサー、演算子、インスタンスコンストラクタ、デストラクタ、および静的コンストラクタ)は無視されることに注意してください。

    • それ以外の場合、namespace-or-type-nameが発生する名前空間から始まり、それぞれを囲む名前空間(存在する場合)に続き、グローバル名前空間で終わる、エンティティが見つかるまで次の手順が評価されます。

    • 名前空間に指定された名前の名前空間メンバーが含まれている場合、namespace-or-type-nameはそのメンバーを参照し、メンバーに応じて、名前空間またはタイプとして分類されます。

    • それ以外の場合、namespace-or-type-nameが発生する場所を囲む対応する名前空間宣言が名前空間にある場合、次のようになります。

    • 名前空間宣言に、指定された名前をインポートされた名前空間またはタイプに関連付けるusing-alias-directiveが含まれる場合、namespace-or-type-nameその名前空間またはタイプを参照します。

    • それ以外の場合、名前空間宣言のusing-namespace-directivesによってインポートされた名前空間に指定された名前の型が1つだけ含まれている場合、namespace-or-type-nameはその型を参照します。
      ...

(太字は私のものです)これは、Fooを解決するときに、CompanyName.Foo(最初のボールドビット)は、usingディレクティブ(2番目のボールドビルド)と照合する前に発生します。

5
Igor Zevaka

タイプがネームスペースのルートと同じ名前である別のクラスライブラリのクラスを参照しているときに、この問題が発生しました。当初、別のコンソールアプリプロジェクトでこのタイプを参照する場合、問題はなく、すべて正常にコンパイルされていました。ただし、Windowsサービスプロジェクトからの参照はis a 'namespace' but is used like a 'type'.メッセージを生成していました。 Windowsサービスプロジェクトのターゲットフレームワークが「.NET Framework 4クライアントプロファイル」に設定されていたことが判明しました。これを「.NET Framework 4」に変更すると、エラーがなくなりました。うまくいけば、これは似たような状況で誰かを助ける。

1
David Marchelya

私はc#を初めて使用しますが、ac#アプリケーションを逆コンパイルし、プロジェクトとして保存し、すぐに再コンパイルしようとすると、このエラーに遭遇しました...アプリケーションが最初にコンパイルできた理由は私を超えています.. ..問題と解決策は非常に簡単です:デフォルトで、新しいクラスを追加すると、c#は名前空間内のクラスの名前と同じ名前を名前空間に使用します!!!!!参照している(名前空間またはタイプ)を明示的に示すいくつかの隠された識別子は、コンパイラーが違いを見分けることができません!!!!! doh!c#!!!! ...の解決方法:代わりに:千の名前を変更し、すべての修正を再確認し、プロジェクトを実行します。目の前にエラーのリストがある場合は、それぞれを順番にクリックして各問題に進みます。問題の「foo」が表示されたら、「foo」の後にドット(。)を入力します:foo。 ..これにより、含まれるクラスのメニューが表示されます。このリストで、「foo」をダブルクリック(または単に名前を再入力)して、元の「foo」を「foo.foo」に変更します...エラーと解決された問題ごとにこれを行います!!!出来上がり!!!!複雑な名前を持つアプリケーション全体に対してこれを行いましたが、うまくいきました!ハッピーコーディング! -ティムH.

1
Tim H.

どうしてできないの?

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

CompanyとFooを区切るためにドット表記を使用したため、Company.Fooではなく、入れ子になったFoo名前空間を持つCompany名前空間を暗黙的に作成しています。

これがうまくいかない理由です:

namespace Company.Foo
{
}

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

Fooに最も近いのは、Companyネームスペース内のネストされた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 */
}

編集

イゴールは同じことを言ったが、より技術的だった。

0
scottm

これは、名前空間と同じ名前のクラスがあるときに単体テストを生成する場合にも発生します。ここでエリック・リッパートが説明したように、あなたは決してすべきではありません:

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

0
Jason Axelson