web-dev-qa-db-ja.com

.NETで汎用インデクサーを定義できないのはなぜですか?

.NETでジェネリックインデクサーを作成できないのはなぜですか?

次のコードはコンパイラエラーをスローします。

   public T this<T>[string key]
   {
      get { /* Return generic type T. */ }
   }

これは、ジェネリックメンバーコレクションのジェネリックインデクサーを作成できないことを意味しますか?

42
Igor Zelaya

私がこれを使用できると考えることができる唯一のものは、これらの線に沿ったものです:

var settings = ConfigurationSection.AppSettings;
var connectionString = settings<string>["connectionString"];
var timeout = settings<int>["timeout"];

しかし、これは実際には何も購入しません。 ((int)settings["timeout"]のように)丸括弧を山括弧に置き換えたばかりですが、自由に実行できるため、追加の型安全性はありません。

var timeout = settings<int>["connectionString"];

厳密に型指定されているが静的に型指定されていないものがある場合は、dynamicキーワードを指定してC#4.0まで待つことをお勧めします。

1
Anton Gogolev

これが役立つ場所です。オプションを宣言するための強い型のOptionKey<T>があるとします。

public static class DefaultOptions
{
    public static OptionKey<bool> SomeBooleanOption { get; }
    public static OptionKey<int> SomeIntegerOption { get; }
}

オプションがIOptionsインターフェースを介して公開される場所:

public interface IOptions
{
    /* since options have a default value that can be returned if nothing's
     * been set for the key, it'd be Nice to use the property instead of the
     * pair of methods.
     */
    T this<T>[OptionKey<T> key]
    {
        get;
        set;
    }

    T GetOptionValue<T>(OptionKey<T> key);
    void SetOptionValue<T>(OptionKey<T> key, T value);
}

次に、コードは汎用インデクサーをニースの強く型付けされたオプションストアとして使用できます。

void Foo()
{
    IOptions o = ...;
    o[DefaultOptions.SomeBooleanOption] = true;
    int integerValue = o[DefaultOptions.SomeIntegerOption];
}
28
Sam Harwell

C#2.0/3.0ではプロパティをジェネリックにすることはできないため、ジェネリックインデクサーを使用することはできません。

18
Kev

理由はわかりませんが、インデクサーは単なる構文糖衣です。代わりにジェネリックメソッドを記述すれば、同じ機能が得られます。例えば:

   public T GetItem<T>(string key)
   {
      /* Return generic type T. */
   }
17
davogones

あなたはできる;宣言から<T>の部分を削除するだけで、正常に機能します。つまり.

public T this[string key]
{
   get { /* Return generic type T. */ }
}

(クラスがTという名前の型パラメーターを持つジェネリックであると仮定します)。

11
Greg Beech

「インデックス付き」アイテムへの直接参照を渡さずにインデクサーを持つ機能が好きです。以下に簡単な「コールバック」インデクサークラスを作成しました...

R =インデクサーから返されたタイプP =インデクサーに渡されたタイプ

インデクサーが実際に行うのは、操作をデプロイヤーに渡し、実際に発生して返されるものを管理できるようにすることだけです。

public class GeneralIndexer<R,P>
    {
        // Delegates
        public delegate R gen_get(P parm);
        public delegate void gen_set(P parm, R value);
        public delegate P[] key_get();

        // Events
        public event gen_get GetEvent;
        public event gen_set SetEvent;
        public event key_get KeyRequest;

        public R this[P parm]
        {
            get { return GetEvent.Invoke(parm); }
            set { SetEvent.Invoke(parm, value); }
        }

        public P[] Keys
        {
            get
            {
                return KeyRequest.Invoke();
            }
        }

    }

プログラムまたはクラスで使用するには:

private GeneralIndexer<TimeSpan, string> TimeIndex = new GeneralIndexer<TimeSpan,string>();

{
            TimeIndex.GetEvent += new GeneralIndexer<TimeSpan, string>.gen_get(TimeIndex_GetEvent);
            TimeIndex.SetEvent += new GeneralIndexer<TimeSpan, string>.gen_set(TimeIndex_SetEvent);
            TimeIndex.KeyRequest += new GeneralIndexer<TimeSpan, string>.key_get(TimeIndex_KeyRequest);

}

リストへのアクセスを監視したり、何かにアクセスしたときに特別な操作を実行したりする場合は特に、チャンピオンのように機能します。

2
Stuart Peacock

最近のCシャープでは、戻り値の型を「動的」として宣言できます。これは「オブジェクト」を使用するのと同じですが、C#ランタイムを使用すると、コードでそれが思っているタイプであるかのように使用でき、実行時に正しいことを確認できます...

0