読者です 読者をやめる 読者になる 読者になる

ほげほげー

C#メインで困ったことや面白く感じたことをメモしていきます。

C#におけるIndexer詳説(ポロリもあるよ!)

タイトル詐欺してみたかったんです。
でも恥ずかしくなったのでポロリもあるよって付け加えて更に恥ずかしくなってるのです。
内容は極々薄いので悪しからず。

Indexer(インデクサ)って?

var value = array[index];

var value = dictionary[key];

要するに

  • 配列の要素indexを指定
  • DictionaryのKeyを指定

して要素を取り出す時に使うアレです。

実装方法

C#では下記のような記述で実装します。

    public class Item
    {
        readonly List<string> container;

        /* 中略 */

        /// <summary>インデクサー!</summary>
        public string this[int index]
        {
            get { return this.container[index]; }
            set { this.container[index] = value; }
        }
    }

さて、これでインデクサが使えるようになりません。

エラー  216 'Item': メンバー名をそれを囲む型の名前と同じにすることはできません。

こんなビルドエラーが発生します。
はい、ココが今回の覚え書きです。

とある事情でItemと言う名前のメンバーなりクラス名なりを含んだクラスを作成した所、

何故かコンパイル出来ないと言うことで小一時間ハマりました。

何が起こっているのか?

C#ではコンパイル時に中間言語(以下IL)を書き出しますが、
その時に、インデクサ部分がItemと言う名前のプロパティに置き換わっているため

  • 同名のプロパティを2つ定義することは出来ない
  • クラス名と同名のプロパティを定義することは出来ない

と言ういずれかの条件にハマるため、コンパイルエラーとなっているわけです。

実際にILを確認してみます。 今回はLINQPadを使いました。*1

こんな感じで試しました。

void Main()
{
    
}

// Define other methods and classes here
public string this[int index]
{
    get { return index.ToString(); }
    set {  }
}

すると、ILの結果は

f:id:tyhe:20140125033038p:plain

となります。
get_Itemとset_Itemが定義されています。

ではItemと言うプロパティを同じように定義してみます。

void Main()
{
    
}

// Define other methods and classes here
public string Item
{
    get { return ""; }
    set {  }
}

この結果

f:id:tyhe:20140125033302p:plain

とゆーILが吐かれました。
こちらも同じくget_Itemとset_Item。

これでItemと言うクラス内でインデクサをコンパイルした際
何故コンパイルエラーになるかが判りました。

回避するにはItemというクラス名か、Itemというプロパティ名を妥協すればいいだけなので簡単ですね!!

とは言え、正直名前は大事なので、もしその単語が使いたいとなった場合、
やはりどうしても何が何でもそれが使いたかったりするわけで。
ではどうするか。

    public class Item
    {
        readonly List<string> container;

        /* 中略 */

        /// <summary>インデクサのコンパイル時の名称を変えればいいじゃない!!</summary>
        [System.Runtime.CompilerServices.IndexerName("MyIndexer")]
        public string this[int index]
        {
            get { return this.container[index]; }
            set { this.container[index] = value; }
        }
    }

とすることで回避が可能になっています。

参考:IndexerNameAttribute クラス (System.Runtime.CompilerServices)

Indexerの項目漁っても載ってないから若干苦戦したので、 同じ問題にぶち当たった同士のタメになれば幸い。

というわけで久しぶりの記事でした。
モチベーションが若干上がってるので、また何かあればちょいちょい書き留めて行きますにー。

でわでわー

*1:お試しな事をするのに非常にお手軽なのでオススメです。無償版だと補完が効きませんが・・・