5

I have a huge array that contains reference type elements, and I want to create a lot of other arrays that essentially just point to specific parts of that one big array.

In other words, I want to create "indexers" or "pointers with lengths".

In C++ it's easy to do so using pointers and for each pointer assign a length, for example create a struct which contains a pointer with a length.

How can I achieve this in C#/.NET?

The whole point is to avoid copying anything, I just want pointers to specific parts in an array that already exists in memory.

Any ideas?

SpaceMonkey
  • 4,143
  • 5
  • 38
  • 60
  • have you tried skip.. take? – Sruti Sep 04 '13 at 19:53
  • If you want you can use pointers – Kamil Budziewski Sep 04 '13 at 19:54
  • 1
    Arrays are indexed into using integers, so obviously a "pointer to a specific place in an array" is an integer. Lengths are also represented as integers. So you are talking about a pair of integers here. You can pack those together in a `Tuple`, or create your own `struct` if you prefer a more descriptive name. – Jon Sep 04 '13 at 19:55
  • @wudzik, I think pointers in c# is just as shunned as `goto` statements. – gunr2171 Sep 04 '13 at 19:57
  • This may be relevant http://stackoverflow.com/questions/2333574/is-there-pointer-in-c-sharp-like-c-is-it-safe , but @Jon describes exactly what I would suggest. – Andrew Savinykh Sep 04 '13 at 19:57
  • If you store your data as an array of objects then you will always pass your array elements be reference by default (It's what C# does) – naed21 Sep 04 '13 at 19:59
  • @naed21: No, C# uses pass-by-value by default... although those elements may be references. See http://pobox.com/~skeet/csharp/parameters.html – Jon Skeet Sep 04 '13 at 20:05

3 Answers3

11

Jon's suggestion of using ArraySegment<T> is likely what you want. If however you are wanting to represent a pointer to the interior of an array, the way you can in C++, here's some code for that. No warranty is expressed or implied, use at your own risk.

This code does not track the "length" of the interior pointer in any way, but it is quite easy to add that feature if you want.

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; }
  }
}

Now we can do stuff like:

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
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I'm surprised that it has all kinds of operators which I wouldn't have expected (the comparison ones in particular) but doesn't implement `IEnumerable` - I'd have expected that iterating over a segment would be more commonly useful than those operators. It makes sense when you look at it as if it were a pointer - less so if you try to consider it as a view onto an array. – Jon Skeet Sep 04 '13 at 19:59
  • @JonSkeet: You make a good point. My intention was not to make this code fully-featured -- I would want a `Length` property and `IEnumerable` as you note and so on. My intention rather was to get a complex hunk of C++ code ported to C# as rapidly as possible so that I could then refactor it at my leisure. The code in question did pointer comparisons, additions and subtractions, so that's all I implemented. – Eric Lippert Sep 04 '13 at 20:02
  • Thanks a lot, I thought of doing that (creating my own class), but I was hoping for a built-in that doesn't waste my time. Jon suggested I use ArraySegment, would it be faster to use this instead? I know I'm being lazy because I can test the performance myself, but I'm guessing you must have tried that already. – SpaceMonkey Sep 04 '13 at 20:17
  • @Spacemonkey: You're the guy who knows what your performance requirements are, not me. Try it and find out! – Eric Lippert Sep 04 '13 at 20:23
  • Or live dangerously and go unsafe (if you use valuetypes) ;p – leppie Sep 04 '13 at 20:23
  • Turns out this way is much faster than using ArraySegment ! – SpaceMonkey Sep 04 '13 at 21:35
  • In this usage, is it possible for me to recast the array pointer "ArrayPtr" back to double[]? I would like to use ArrayPtr as it has access to the indexer which allows me to insert logic directly to the element being worked on. But I would like to provide external interface as double[]. – CJC Aug 07 '22 at 10:41
11

It sounds like you're looking for something like ArraySegment<T>. Contrary to my earlier thoughts, it does have an indexer and implement IEnumerable<T> etc - it's just done with explicit interfaces.

Sample code:

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: As noted in comments, ArraySegment<T> is only really "fully functional" in .NET 4.5. The .NET 4 version doesn't implement any interfaces.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • This struct, `ArraySegment<>`, was extended a lot in .NET 4.5. Before 4.5 it lacked a lot of "natural" functionality. – Jeppe Stig Nielsen Sep 04 '13 at 20:06
  • @JeppeStigNielsen: Ah, that makes a lot of sense. Thanks, will note that. – Jon Skeet Sep 04 '13 at 20:08
  • Thanks so much, I didn't expect to get an answer from you ! One question though, does it come with any performance penalty to uses indexers with ArraySegment? – SpaceMonkey Sep 04 '13 at 20:15
  • 1
    @Spacemonkey: Yes, I'd expect there to be a performance penalty. Because the interfaces are explicitly implemented, you *have* to go through the interfaces, which means a virtual function call. Array element access could be significantly quicker. However, I'd expect that *most* applications wouldn't actually have their bottleneck at this sort of access anyway. As ever, set some performance criteria and then test. – Jon Skeet Sep 04 '13 at 20:17
1

You could use LINQ:

yourArray.Skip(startIndex).Take(numberToTake)

The query is lazily evaluated.

gunr2171
  • 16,104
  • 25
  • 61
  • 88
Нет войне
  • 1,139
  • 1
  • 11
  • 21