11

C#、VS 2010。誰か、var以下のコードで使用できない理由を説明してください!

var props = TypeDescriptor.GetProperties(adapter);

// error CS1061: 'object' does not contain a definition for 'DisplayName'
foreach (var prop in props)
{
    string name = prop.DisplayName;
}

// No error
foreach (PropertyDescriptor prop in props)
{
    string name = prop.DisplayName;
}

TypeDescriptor.GetPropertiesPropertyDescriptorCollectionのインスタンスを含む を返しますPropertyDescriptor。コンパイラがこれを認識できないのはなぜですか?

4

5 に答える 5

23

TypeDescriptor.GetPropertiesGetEnumerator非ジェネリックを返す実装のみを持つクラスを返しますIEnumerator。そのCurrentプロパティの型はobject- であり、それがコンパイラが推測できる唯一の型であるため、それがprop変数の型になります。

の型として代わりにforeach使用する2 番目は、実際に からへの変換を実行します。PropertyDescriptorvarpropobjectPropertyDescriptor

次のコードを想定します。

PropertyDescriptor x = // get from somewhere;
object o = x;
PropertyDescriptor y = (PropertyDescriptor)o;

foreach各項目の 2 番目のループでも同じことが起こっています。


を追加して、その戻り値の実装でCast<PropertyDescriptor>()ジェネリックを取得できます。これを行うと、ループで使用できます:IEnumerable<T>GetEnumeratorIEnumerator<T>varforeach

var props = TypeDescriptor.GetProperties(adapter).Cast<PropertyDescriptor>();

// No error
foreach (var prop in props)
{
    string name = prop.DisplayName;
}
于 2013-02-14T11:46:05.037 に答える
8

PropertyDescriptorCollection は のみを実装IEnumerableするため、コンパイラはそれに含まれる要素のタイプが であることのみを認識しますobject。foreach ループで型を指定すると、コンパイラは指定した型へのキャストを挿入します。

Cast<T>拡張メソッド onを使用して、IEnumerable各アイテムを特定の型にキャストできます。

using System.Linq;
...
IEnumerable<PropertyDescriptor> descriptors = props.Cast<PropertyDescriptor>();
于 2013-02-14T11:47:12.680 に答える
5

を実装せず、 のみをTypeDescriptor.GetProperties返すためです。PropertyDescriptorCollectionIEnumerable<PropertyDescriptor>IEnumerable

したがって、コンパイル時にプロパティを持たないprop単なるです。objectDisplayName

したがって、次のようにタイプを明示的に指定する必要がありますforeach

foreach (PropertyDescriptor prop in props)
{
    string name = prop.DisplayName;
}
于 2013-02-14T11:46:54.187 に答える
3

PropertyDescriptorCollectionを実装していますが、sを列挙していることだけがわかるIEnumerableわけではありません 。というわけで推測です。IEnumerable<PropertyDescriptor>objectvar

foreachには、反復変数の型が何であれ、隠しキャストを実行する機能が常にあったため、2 番目のバージョンが機能するのはそのためです。ジェネリック以前の時代には、このように機能しなければなりませんでした。

于 2013-02-14T11:46:45.613 に答える
2

ここで説明する内容は、C# 言語仕様のセクション「foreach ステートメント」で正式に詳細に説明されています。

暗黙的に型指定された反復変数foreachステートメント (つまり) で使用する場合var、これは非常に良いことですが、コンパイラがvar意味するものを見つける方法は次のとおりです。

最初に、 のキーワードの右側にある式のコンパイル時の型をチェックします。(または類似の配列型の場合は、特別な規則が適用されます。それが の場合は、別の規則があります。)inforeachPropertyDescriptor[]PropertyDescriptor[,,,]dynamic

その型に、非静的、非ジェネリックであり、ゼロのパラメーターをGetEnumerator受け取るオーバーロードを使用して (その大文字化で)正確に呼び出されるメソッドがあるかどうかを確認します。publicそうであれば、このメソッドの戻り値の型を調べます (ここではまだコンパイル時の型について話しているので、宣言された戻り値の型です)。この型には、メソッドMoveNext()とプロパティが必要Currentです。次に、プロパティ タイプを受け取り、それを要素タイプCurrentとして使用します。したがって、あなたはこのタイプの略です。var

これがどのように機能するかを示すために、私はこれを書きました:

    class Foreachable
    {
        public MyEnumeratorType GetEnumerator()  // OK, public, non-static, non-generic, zero arguments
        {
            return default(MyEnumeratorType);
        }

    }

    struct MyEnumeratorType
    {
        public int Current
        {
            get { return 42; }
        }

        public bool MoveNext()
        {
            return true;
        }
    }

    static class Test
    {
        static void Main()
        {
            var coll = new Foreachable();

            foreach (var x in coll)   // mouse-over 'var' to see it translates to 'int'
            {
                Console.WriteLine(x);
            }
        }
    }

私の場合は、プロパティのタイプにより( ) にvarなっています。intSystem.Int32Current

あなたの場合、コンパイル時のタイプpropsPropertyDescriptorCollection. この型には、GetEnumerator()必要に応じてパブリックで非静的な があります。メソッドの戻り値の型は、System.Collections.IEnumeratorこの場合に見られます。このIEnumeratorタイプには必要なCurrentプロパティがあり、このプロパティのタイプは であることがわかりますObject。だからそこから来ました!

(元は .NET 1 用に作成された多くのクラスがこの設計を持っています。強い型付けはありませんforeach。)

型が(ジェネリック) or/and (非ジェネリック) を実装し、これら 2 つのインターフェイスのいずれが「暗黙的に」実装されている場合 (通常は、明示的なインターフェイスの実装ではありません)、その型には確実にpublic であり非公開の があることに注意してください。-static、非ジェネリック、引数なし。その public メソッドは によって使用されます。IEnumerable<out T>IEnumerableGetEnumeratorforeach

しようとしている式のコンパイル時の型にforeachパブリック インスタンス メソッドがない場合GetEnumerator()(型パラメーターも値パラメーターもありません)、コンパイラは型がIEnumerable<Something>or (else) に変換可能かどうかを確認しIEnumerableます。IEnumerable<out T>は で共変であるため、T多くの場合、多くの場合に適用されます。これは、仕様 (バージョン 5.0) で少し紛らわしく説明されています。タイプが次のようになる可能性もあります。SomethingIEnumerable<Something>

    class Foreachable : IEnumerable<Animal>, IEnumerable<Giraffe>
    {
        // lots of stuff goes here
    }

参照型AnimalGiraffe予想される継承関係の場合、仕様バージョン 5.0から、このクラス (コンパイル時の型) をforeach編集できないことが明確ではありません。

于 2013-02-16T22:07:16.393 に答える