2013-09-04 6 views
5

私は参照型要素を含む巨大な配列を持っています、そして、私は本質的にその1つの大きな配列の特定の部分を指し示す多くの他の配列を作成したいと思います。別の配列の一部だけを指す配列を作成しますか?

つまり、「インデクサ」または「長さのポインタ」を作成したいと考えています。

C++では、ポインタを使用するのは簡単ですし、ポインタごとに長さを割り当てることができます。たとえば、長さのポインタを含む構造体を作成します。

C#/ .NETでこれをどのように達成できますか?

何かをコピーしないようにすることがポイントです。メモリ内に既に存在する配列の特定の部分へのポインタが必要なだけです。

アイデア?

+0

あなたはスキップしてみましたか?take? – Sruti

+0

ポインタを使用できるようにしたい場合 – wudzik

+1

配列は整数を使用して索引付けされるため、明らかに「配列内の特定の場所へのポインタ」は整数です。長さも整数として表されます。だからここでは整数のペアについて話しています。それらをまとめて 'Tuple 'にするか、よりわかりやすい名前が好きな場合は独自の 'struct'を作成することができます。 – Jon

答えて

11

ジョンの提案はArraySegment<T>を使用している可能性が高いです。しかし、配列の内部へのポインタを表現したいのであれば、C++のやり方では、そのためのコードがあります。いかなる保証も表明されていません。お客様の責任において使用してください。

このコードでは、内部ポインタの長さは決して追跡されませんが、必要に応じてその機能を追加するのは簡単です。それはあなたがArraySegment<T>ような何かを探しているように聞こえる

double[] arr = new double[10]; 
var p0 = (ArrayPtr<double>)arr; 
var p5 = p0 + 5; 
p5[0] = 123.4; // sets arr[5] to 123.4 
var p7 = p0 + 7; 
int diff = p7 - p5; // 2 
+0

私はそれが私が期待していないすべての種類の演算子(特に比較のもの)を持っているのに、IEnumerable を実装していないことに驚いています。これらの演算子よりも一般的に便利です。あなたがそれをポインタ上にあるかのように見ても、それを配列上のビューとして見なすと意味があります。 –

+0

@JonSkeet:あなたは良い点を作っています。私の意図は、このコードを完全に機能させることではありませんでした - 私はあなたに注意するように 'Length'プロパティと' IEnumerable 'が必要です。私の意図は、むしろできるだけ速くC#に移植された複雑なC++コードを取得して、私が余暇でそれをリファクタリングできるようにすることでした。問題のコードはポインタの比較、加算、減算を行ったので、これがすべて実装されました。 –

+0

ありがとう、私は自分のクラスを作ることを考えていましたが、自分の時間を無駄にしないビルトインを望んでいました。 JonはArraySegmentを使用するように提案しましたが、代わりにこれを使用する方が早いでしょうか? 私はパフォーマンスを自分自身でテストできるので私は怠惰だと知っていますが、私はあなたがすでにそれを試したに違いないと思っています。 –

12

:よう

internal struct ArrayPtr<T> 
{ 
    public static ArrayPtr<T> Null { get { return default(ArrayPtr<T>); } } 
    private readonly T[] source; 
    private readonly int index; 

    private ArrayPtr(ArrayPtr<T> old, int delta) 
    { 
    this.source = old.source; 
    this.index = old.index + delta; 
    Debug.Assert(index >= 0); 
    Debug.Assert(index == 0 || this.source != null && index < this.source.Length); 
    } 

    public ArrayPtr(T[] source) 
    { 
    this.source = source; 
    index = 0; 
    } 

    public bool IsNull() 
    { 
    return this.source == null; 
    } 

    public static bool operator <(ArrayPtr<T> a, ArrayPtr<T> b) 
    { 
    Debug.Assert(Object.ReferenceEquals(a.source, b.source)); 
    return a.index < b.index; 
    } 

    public static bool operator >(ArrayPtr<T> a, ArrayPtr<T> b) 
    { 
    Debug.Assert(Object.ReferenceEquals(a.source, b.source)); 
    return a.index > b.index; 
    } 

    public static bool operator <=(ArrayPtr<T> a, ArrayPtr<T> b) 
    { 
    Debug.Assert(Object.ReferenceEquals(a.source, b.source)); 
    return a.index <= b.index; 
    } 

    public static bool operator >=(ArrayPtr<T> a, ArrayPtr<T> b) 
    { 
    Debug.Assert(Object.ReferenceEquals(a.source, b.source)); 
    return a.index >= b.index; 
    } 

    public static int operator -(ArrayPtr<T> a, ArrayPtr<T> b) 
    { 
    Debug.Assert(Object.ReferenceEquals(a.source, b.source)); 
    return a.index - b.index; 
    } 

    public static ArrayPtr<T> operator +(ArrayPtr<T> a, int count) 
    { 
    return new ArrayPtr<T>(a, +count); 
    } 

    public static ArrayPtr<T> operator -(ArrayPtr<T> a, int count) 
    { 
    return new ArrayPtr<T>(a, -count); 
    } 

    public static ArrayPtr<T> operator ++(ArrayPtr<T> a) 
    { 
    return a + 1; 
    } 

    public static ArrayPtr<T> operator --(ArrayPtr<T> a) 
    { 
    return a - 1; 
    } 

    public static implicit operator ArrayPtr<T>(T[] x) 
    { 
    return new ArrayPtr<T>(x); 
    } 

    public static bool operator ==(ArrayPtr<T> x, ArrayPtr<T> y) 
    { 
    return x.source == y.source && x.index == y.index; 
    } 

    public static bool operator !=(ArrayPtr<T> x, ArrayPtr<T> y) 
    { 
    return !(x == y); 
    } 

    public override bool Equals(object x) 
    { 
    if (x == null) return this.source == null; 
    var ptr = x as ArrayPtr<T>?; 
    if (!ptr.HasValue) return false; 
    return this == ptr.Value; 
    } 

    public override int GetHashCode() 
    { 
    unchecked 
    { 
     int hash = this.source == null ? 0 : this.source.GetHashCode(); 
     return hash + this.index; 
    } 
    } 

    public T this[int index] 
    { 
    get { return source[index + this.index]; } 
    set { source[index + this.index] = value; } 
    } 
} 

今、私たちはものを行うことができます。私の以前の思想とは逆に、にインデクサーがあり、IEnumerable<T>などを実装しています。これは明示的なインターフェイスで行われています。

サンプルコード:

using System; 
using System.Collections.Generic; 

static class Test 
{ 
    static void Main() 
    { 
     string[] original = { "The", "quick", "brown", "fox", "jumped", "over", 
       "the", "lazy", "dog" }; 

     IList<string> segment = new ArraySegment<string>(original, 3, 4); 
     Console.WriteLine(segment[2]); // over 
     foreach (var word in segment) 
     { 
      Console.WriteLine(word); // fox jumped over the 
     } 
    } 
} 

EDIT:コメントで述べたように、ArraySegment<T>は、.NET 4.5でだけは本当に "完全に機能" です。 .NET 4 versionは、の任意のインターフェイスを実装していません。

+0

この構造体 'ArraySegment <>'は、.NET 4.5で大幅に拡張されました。 4.5より前には、 "自然な"機能がたくさんありませんでした。 –

+0

@JeppeStigNielsen:ああ、それはたくさんの意味があります。ありがとう、それに注意します。 –

+0

ありがとう、私はあなたから答えを得ることを期待していない! しかし、1つの質問では、ArraySegmentでインデクサーを使用するとパフォーマンスが低下することはありますか? –

1

あなたは、LINQを使用することができます。

yourArray.Skip(startIndex).Take(numberToTake) 

クエリが遅延評価されます。

関連する問題