13

現在、クラス定義内に「インデックス付き」プロパティを実装しようとしています。

たとえば、次のクラスがあります。

public class TestClass
{
   private int[] ids = null;

   public string Name { get; set; }
   public string Description { get; set; }
   public int[] Ids { 
      get
      {
         //Do some magic and return an array of ints 
         //(count = 5 - in this example in real its not fixed)
         return _ids;
      }
   }
}

今、私はこのクラスを次のように使用したいと思っています:

private void DoSomething()
{
   var testClass = GetSomeTestClass();
   //work with the ids

   for (int i = 0; i < 10; i++) //I know I could say i < Ids.Length, its just an example
   {
      int? id = testClass.Ids[i];
      //this will result, in a out of bound exception when i reaches 5 but I wish for it to return a null like a "safe" index call ?!?
   }
}

したがって、try catch で何度もラップする必要なく、null になる安全なインデックス呼び出しはありますか。

クラスインデックスを使用したくないもう1つのことは、このように機能するさまざまなタイプ(int、string、bool、カスタムクラスなど)のプロパティがいくつか必要なためです。

(繰り返しますが、for は単純な例にすぎません。この場合、「i < Ids.Length」と言えます)

4

8 に答える 8

10

たとえば、すでに null 非許容型のデータにのみ関心がある場合はstruct、単純な拡張メソッドを使用できます。

public static class ArrayExt
{
    public static Nullable<T> GetValueOrNull(this T[] array, int index) where T: struct
    {
        return array.Length < index ? new Nullable<T>(array[index]) : null;
    }
}

これにより、単純に呼び出すことができます

int? id = testClass.Ids.GetValueOrNull(i);

ただし、任意の数の型をサポートする必要がある場合、私の提案は、配列の周りにラッパーを実装し、データへのアクセス方法を制御することです。

public class SafeArray<T>
{
    private T[] items;

    public SafeArray(int capacity)
    {
        items = new T[capacity];
    }

    public object this[int index]
    {
        get
        {
            return index < items.Length ? (object)items[index] : null;
        }
        set
        {
            items[index] = (T)value;
        }
    }
}

public class TestClass
{
    public TestClass()
    {
        Ids = new SafeArray<int>(5);
        Instances = new SafeArray<MyClass>(5);
    }
    ...
    public SafeArray<int> Ids { get; private set; }

    public SafeArray<MyClass> Instances { get; private set; }
}

このアプローチの鍵はobject、戻り値の型として使用することです。これにより、受信側で期待される型にデータをキャスト (または値型を使用する場合はボックス化/ボックス化解除) できます。

for (int i = 0; i < 10; i++)
{
    // we need an explicit cast to un-box value types
    var id = (int?)testClass.Ids[i];
    // any class is already of type object so we don't need a cast
    // however, if we want to cast to original type we can use explicit variable declarations e.g.
    MyClass instance = testClass.Instances[i];
}
于 2013-11-06T10:27:15.237 に答える
3

OK、まったく新しいアプローチ。いくつかの可能なタイプがあり、「ジョーカー」メソッドが必要なため、値をキー/値コレクションとしてクラスに格納すると、そのようなメソッドが可能になります。

まず、値を内部に保存するには:

public class TestClass
{
     private Dictionary<Type, Array> _values = new Dictionary<Type, Array>();
}

次に、そのコレクションに実際のデータを入力します。

_values.Add(typeof(int?), new int[] { 1, 2, 3 });
_values.Add(typeof(string), new string[] { "a", "b", "c", "d", "e" });

そして最後にジョーカー法:

public T Get<T>(int index)
{
    Type type = typeof(T);
    Array array;
    if (_values.TryGetValue(type, out array))
    {
        if (index >= 0 && index < array.Length)
        {
            return (T)array.GetValue(index);
        }
    }
    return default(T);
}

使用法:

for (int i = 0; i < 10; i++)
{
  int? id = testClass.Get<int?>(i);
  string name = testClass.Get<string>(i);
  //...
}
于 2013-11-06T10:41:07.923 に答える
1

ここでできることは、次のことだけです。

if (i >= array.Length) return null;
else return array[i];

または、?演算子を使用して:

return (i >= array.Length) ? null : array[i];
于 2013-11-06T10:11:07.817 に答える