34

C# では、インデックス付きプロパティが非常に便利だと思います。例えば:

var myObj = new MyClass();
myObj[42] = "hello"; 
Console.WriteLine(myObj[42]);

ただし、私が知る限り、インデックス作成をサポートするフィールドをサポートする構文糖衣はありません (間違っている場合は修正してください)。例えば:

var myObj = new MyClass();
myObj.field[42] = "hello"; 
Console.WriteLine(myObj.field[42]);

これが必要な理由は、クラスで既に index プロパティを使用しているためですが 、次のようGetNumX()に、、、GetX()およびSetX()関数があります。

public int NumTargetSlots {  
    get { return _Maker.NumRefs; }  
}
public ReferenceTarget GetTarget(int n) {
    return ReferenceTarget.Create(_Maker.GetReference(n));
}
public void SetTarget(int n, ReferenceTarget rt) {
    _Maker.ReplaceReference(n, rt._Target, true);
}

おそらくお分かりのように、これらを 1 つのインデックス可能なフィールド プロパティとして公開する方が理にかなっています。シンタックス シュガーが必要なときはいつでも、これを実現するカスタム クラスを作成できますが、定型コードはすべて不必要に思えます。

そこで、ボイラープレートをカプセル化し、インデックス付けできるプロパティを簡単に作成できるようにするカスタム クラスを作成しました。このようにして、次のように新しいプロパティを追加できます。

public IndexedProperty<ReferenceTarget> TargetArray  {
    get { 
       return new IndexedProperty<int, ReferenceTarget>(
           (int n) => GetTarget(n), 
           (int n, ReferenceTarget rt) => SetTarget(n, rt));
       }
}

この新しい IndexedProperty クラスのコードは次のようになります。

public class IndexedProperty<IndexT, ValueT>
{
    Action<IndexT, ValueT> setAction;
    Func<IndexT, ValueT> getFunc;

    public IndexedProperty(Func<IndexT, ValueT> getFunc, Action<IndexT, ValueT> setAction)
    {
        this.getFunc = getFunc;
        this.setAction = setAction;
    }

    public ValueT this[IndexT i]
    {
        get {
            return getFunc(i);
        }
        set {
            setAction(i, value);
        }
    }
}

だから私の質問は:これをすべて行うためのより良い方法はありますか?

具体的に言うと、C# でインデックス可能なフィールド プロパティを作成するためのより慣用的な方法はありますか? そうでない場合、どうすればIndexedPropertyクラスを改善できますか?

編集:さらに調査した結果、Jon Skeet はこれを「名前付きインデクサー」と呼んでいます。

4

6 に答える 6

17

2022年の編集:これは引き続き投票を受けていますが、プロパティが頻繁にヒットした場合、ガベージコレクションを大規模に理想的ではない方法でプッシュするため、今日使用するものではないでしょう。これは複雑なトピックだったことを覚えています。今は深く調査するつもりはありませんが、インデクサーが今日この問題を解決できるかどうか疑問に思っています。参照: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/


あなたのアイデアは役に立つと思ったので、拡張しました。あなたの質問に正確に答えているかどうかわからないので、これは技術的には適切な答えではないかもしれませんが、プロパティインデクサーを探してここに来た人には役立つかもしれないと思いました.

まず、取得専用プロパティと設定専用プロパティをサポートできるようにする必要があったため、これらのシナリオ用にコードを少し変更しました。

Get と Set (非常に小さな変更):

public class IndexedProperty<TIndex, TValue>
{
    readonly Action<TIndex, TValue> SetAction;
    readonly Func<TIndex, TValue> GetFunc;

    public IndexedProperty(Func<TIndex, TValue> getFunc, Action<TIndex, TValue> setAction)
    {
        this.GetFunc = getFunc;
        this.SetAction = setAction;
    }

    public TValue this[TIndex i]
    {
        get
        {
            return GetFunc(i);
        }
        set
        {
            SetAction(i, value);
        }
    }
}

取得のみ:

public class ReadOnlyIndexedProperty<TIndex, TValue>
{
    readonly Func<TIndex, TValue> GetFunc;

    public ReadOnlyIndexedProperty(Func<TIndex, TValue> getFunc)
    {
        this.GetFunc = getFunc;
    }

    public TValue this[TIndex i]
    {
        get
        {
            return GetFunc(i);
        }
    }
}

セットのみ:

public class WriteOnlyIndexedProperty<TIndex, TValue>
{
    readonly Action<TIndex, TValue> SetAction;

    public WriteOnlyIndexedProperty(Action<TIndex, TValue> setAction)
    {
        this.SetAction = setAction;
    }

    public TValue this[TIndex i]
    {
        set 
        {
            SetAction(i, value);
        }
    }
}

簡単な使用例を次に示します。Collection を継承し、Jon Skeet が呼んだ名前付きインデクサーを作成します。この例は単純にすることを目的としており、実用的ではありません。

public class ExampleCollection<T> : Collection<T>
{
    public IndexedProperty<int, T> ExampleProperty
    {
        get
        {
            return new IndexedProperty<int, T>(GetIndex, SetIndex);
        }
    }

    private T GetIndex(int index)
    {
        return this[index];
    }
    private void SetIndex(int index, T value)
    {
        this[index] = value;
    }
}

ワイルドな ExampleCollection

この急いで作成された単体テストは、プロジェクトで ExampleCollection を使用したときにどのように見えるかを示しています。

[TestClass]
public class IndexPropertyTests
{
    [TestMethod]
    public void IndexPropertyTest()
    {
        var MyExample = new ExampleCollection<string>();
        MyExample.Add("a");
        MyExample.Add("b");

        Assert.IsTrue(MyExample.ExampleProperty[0] == "a");
        Assert.IsTrue(MyExample.ExampleProperty[1] == "b");

        MyExample.ExampleProperty[0] = "c";

        Assert.IsTrue(MyExample.ExampleProperty[0] == "c");

    }
}

最後に、get-only バージョンと set-only バージョンを使用する場合は、次のようになります。

    public ReadOnlyIndexedProperty<int, T> ExampleProperty
    {
        get
        {
            return new ReadOnlyIndexedProperty<int, T>(GetIndex);
        }
    }

または:

    public WriteOnlyIndexedProperty<int, T> ExampleProperty
    {
        get
        {
            return new WriteOnlyIndexedProperty<int, T>(SetIndex);
        }
    }

どちらの場合も、結果は、取得専用/設定専用プロパティが期待する動作と同じように機能します。

于 2013-07-12T15:46:17.503 に答える
9

さて、最も簡単なのは、プロパティにを実装するオブジェクトを返すようにすることIListです。

IListを実装しているからといって、それ自体がコレクションであることを意味するのではなく、特定のメソッドを実装していることを忘れないでください。

于 2010-07-27T14:30:40.903 に答える
7

あなたが投稿したデザインは、インターフェースを定義するという1つの違いを除いて、進むべき道だと思います:

public interface IIndexed<IndexT, ValueT>
{
    ValueT this[IndexT i] { get; set; }
}

そして、一般的なケースでは、元の質問に入力したクラスを使用します(このインターフェイスを実装します)。

基本クラス ライブラリが適切なインターフェイスを提供してくれればよいのですが、そうではありません。ここで IList を返すのは倒錯です。

于 2010-07-27T18:00:18.087 に答える
5

これはあなたの質問には答えませんが、CIL があなたが説明したようなプロパティの作成をサポートしていることに注意するのは興味深いことです。一部の言語 (たとえば、F#) では、そのような方法でもプロパティを定義できます。

C#のthis[]インデクサーはItem、アプリをビルドするときに名前が変更される、これらのうちの 1 つの特定のインスタンスにすぎません。C# コンパイラはこれを読み取る方法しか認識していないためTarget、F# ライブラリで呼び出される "名前付きインデクサー" を記述し、それを C# で使用しようとする場合、プロパティにアクセスできる唯一の方法はメソッド... get_Target(int)void set_Target(int, ...)メソッドを使用することです。最悪。

于 2010-07-27T18:16:52.447 に答える
3

クラスに IList を継承させないのはなぜですか。インデックスを使用して独自のプロパティを追加するだけです。追加機能と削除機能は引き続き使用できますが、それらを使用しないことは不正ではありません。さらに、それらをさらに先に進めておくと便利な場合があります。

リストと配列の詳細については、次を参照してください: 配列または List<> を使用する方が適切ですか?

編集:

MSDN には、参照したいインデックス プロパティに関する記事があります。面倒なだけで複雑には見えません。

http://msdn.microsoft.com/en-us/library/aa288464(VS.71).aspx

代替の Add メソッドを作成できる別のオプションがありますが、オブジェクトのタイプによっては、add メソッドが常に呼び出されるとは限りません。ここで説明:

C# で List<T> の Add メソッドをオーバーライドするにはどうすればよいですか?

編集2:最初の投稿に似ています

クラスに非表示のリスト オブジェクトを用意してから、データを取得するための独自のメソッドを作成してみませんか。そうすれば、追加と削除は表示されず、リストは既にインデックス化されています。

また、「名前付きインデクサー」とは、行[「My_Column_Name」]に相当するものを探しているという意味ですか。そのプロパティを実装する基本的な方法を示しているように見えるので、私が見つけたMSDNの記事が役立つかもしれません。

http://msdn.microsoft.com/en-us/library/146h6tk5.aspx

class Test
    {
        private List<T> index;
        public T this[string name]{ get; set; }
        public T this[int i]
        {
            get
            {
                return index[i];
            }
            set
            {
                index[i] = value;
            }
        }
    }
于 2010-07-27T14:42:00.030 に答える