11

オブジェクトに詰め込むことができるオブジェクト配列のオブジェクト配列を返すサードパーティのライブラリがあります[]:

object[] arr = myLib.GetData(...);

結果の配列は object[] エントリで構成されるため、戻り値は、行を表す外側の配列と、一部のフィールドが入力されないフィールド値を含む内側の配列 (ギザギザ配列) を持つある種のレコードセットと考えることができます。 . 個々のフィールドにアクセスするには、次のようにキャストする必要があります。

int i = (int) ((object[])arr[row])[col];//access a field containing an int

私は怠け者なので、次のような要素にアクセスしたいと思います。

int i = (int) arr[row][col];

これを行うには、次の Linq クエリを使用します。

object[] result = myLib.GetData(...);
object[][] arr = result.Select(o => (object[])o ).ToArray();

のような単純なキャストを使用してみましobject[][] arr = (object[][])result;たが、実行時エラーで失敗します。

さて、私の質問:

  • これを行う簡単な方法はありますか?気の利いたキャストがそのトリックをやるべきだと感じていますか?
  • また、キャストを節約するためだけに多くのデータを再形成する必要があるため、パフォーマンスが心配ですが、これは本当に価値があるのでしょうか?

編集: 迅速な回答をありがとうございました。
@ジェームズ:あなたの答えが新しいクラスで犯人をラップするのが好きですが、欠点は、ソース配列を取り込むときに常にLinqラッピングを行う必要があり、インデクサーが行と列の両方の値int i = (int) arr[row, col]; を必要とすることです(私は完全な行と同様にobject[] row = arr[row];、申し訳ありませんが最初に投稿しませんでした)。
@Sergiu Mindras: James のように、拡張メソッドはすべてのobject[]変数に適用されるため、少し危険だと感じています。
@Nair:Linqラッパーを使用する必要がなく、 @quetzalcoatlと@Abe Heidebrechtint i = (int) arr[row][col];を使用して個々のフィールドまたは行全体にアクセスできるため、実装にあなたの答えを選択しました: のヒントをありがとう.object[] row = arr[row];
Cast<>()

結論: James の回答と Nair の回答の両方を選択できればよいのですが、上で述べたように、Nair のソリューションは (私が思うに) 最高の柔軟性とパフォーマンスを提供してくれます。上記の Linq ステートメントを使用して内部配列を「平坦化」する関数を追加しました。これは、そのような構造を供給する必要がある他の関数があるためです。

これが私が(大まかに)それをどのように実装したかです(Nairのソリューションから取得:

public class CustomArray { プライベート オブジェクト[] データ; public CustomArray(object[] arr) { データ = arr; }

        //get a row of the data
        public object[] this[int index]
        { get { return (object[]) data[index]; } }

        //get a field from the data
        public object this[int row, int col]
        { get { return ((object[])data[row])[col]; } }

        //get the array as 'real' 2D - Array
        public object[][] Data2D()
        {//this could be cached in case it is accessed more than once
            return data.Select(o => (object[])o ).ToArray()
        }

        static void Main()
        {
            var ca = new CustomArray(new object[] { 
                      new object[] {1,2,3,4,5 },
                      new object[] {1,2,3,4 },
                      new object[] {1,2 } });
            var row = ca[1]; //gets a full row
            int i = (int) ca[2,1]; //gets a field
            int j = (int) ca[2][1]; //gets me the same field
            object[][] arr = ca.Data2D(); //gets the complete array as 2D-array
        }

    }

それでは - もう一度 - ありがとうございました!このサイトを使用することは、常に本当に喜びであり、啓発です。

4

5 に答える 5

7

ラッパークラスを作成して、醜いキャストを隠すことができます。

public class DataWrapper
{
    private readonly object[][] data;

    public DataWrapper(object[] data)
    {
        this.data = data.Select(o => (object[])o ).ToArray();
    }

    public object this[int row, int col]
    {
        get { return this.data[row][col]; }
    }
}

使用法

var data = new DataWrapper(myLib.GetData(...));
int i = (int)data[row, col];

ラッパーをジェネリックにする機会もあります。たとえばDataWrapper<int>、データ コレクションがすべて同じ型になるかどうかはわかりませんでした。返すと、object必要なデータ型キャストを決定するのに十分なジェネリックが維持されます。

于 2013-06-26T14:24:15.990 に答える
3

同様のことを行う投稿された同様の回答はほとんどありません。これは、次のようにアクセスしたい場合にのみ異なります

int i = (int) arr[row][col]; 

アイデアを実証するには

   public class CustomArray
        {
            private object[] _arr;
            public CustomArray(object[] arr)
            {
                _arr = arr;
            }

            public object[] this[int index]
            {
                get
                {
                    // This indexer is very simple, and just returns or sets 
                    // the corresponding element from the internal array. 
                    return (object[]) _arr[index];
                }
            }
            static void Main()
            {
                var c = new CustomArray(new object[] { new object[] {1,2,3,4,5 }, new object[] {1,2,3,4 }, new object[] {1,2 } });
                var a =(int) c[1][2]; //here a will be 4 as you asked.
            }

        }
于 2013-06-26T14:30:47.863 に答える
1

(1) これは、おそらくキーワードを使用した簡潔で簡単な形式でdynamic実行できますが、コンパイル時のチェックを使用します。しかし、object[] を使用することを考えると、それはわずかな代償です。

dynamic results = obj.GetData();
object something = results[0][1];

ただし、コンパイラでチェックしていません。

Select(o => (type)o)(2)専用の機能がある代わりにCast<>

var tmp = items.Select(o => (object[])o).ToArray();
var tmp = items.Cast<object[]>().ToArray();

それらはほとんど同じです。Cast の方が少し速いと思いますが、確認していません。

(3) はい、そのように再形成すると、主にアイテムの量に応じて、パフォーマンスに多少影響します。影響は、要素が多いほど大きくなります。これは主に .ToArray に関連しています。これは、すべてのアイテムを列挙し、追加の配列を作成するためです。このことを考慮:

var results = ((object[])obj.GetData()).Cast<object[]>();

ここでの「結果」はタイプのものIEnumerable<object[]>であり、違いはそれが遅延して列挙されることです。そのため、すべての要素に対する余分な反復がなくなり、一時的な余分な配列がなくなり、オーバーヘッドも最小限になります-すべての要素の手動キャストと同様、とにかくやります..しかし、-最上位の配列にインデックスを付ける機能が失われます。ループすることはできforeachますが、インデックスを作成することはできません[123]

編集:

ジェームズのラッパーの方法は、全体的なパフォーマンスの点でおそらく最高です。読みやすさで一番好きですが、それは個人的な意見です。LINQ の方が好きな人もいるでしょう。しかし私はそれが好きです。ジェームズのラッパーをお勧めします。

于 2013-06-26T14:28:02.223 に答える
1

拡張メソッドを使用できます:

static int getValue(this object[] arr, int col, int row)
{
    return (int) ((object[])arr[row])[col];
}

そして取得する

int requestedValue = arr.getValue(col, row);

arr[int x][int y] 構文についてはわかりません。

編集

ご指摘ありがとうございます

キャスト時に例外が発生しないように、null 許容の int を使用できます。

したがって、メソッドは次のようになります。

static int? getIntValue(this object[] arr, int col, int row)
{
    try
    {
    int? returnVal = ((object[])arr[row])[col] as int;
    return returnVal;
    }
    catch(){ return null; }
}

そして、によって取得することができます

int? requestedValue = arr.getIntValue(col, row);

このようにして、null 許容オブジェクトを取得し、発生したすべての例外が強制的に null を返すようにします

于 2013-06-26T14:29:07.437 に答える
0

Select... の代わりに LINQ Cast 演算子を使用できます。

object[][] arr = result.Cast<object[]>().ToArray()

これは少し冗長ですが、パフォーマンスに関してはほぼ同じです。もう 1 つの方法は、手動で行うことです。

object[][] arr = new object[result.Length][];
for (int i = 0; i < arr.Length; ++i)
    arr[i] = (object[])result[i];
于 2013-06-26T14:27:17.480 に答える