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?

7
  • have you tried skip.. take? Commented Sep 4, 2013 at 19:53
  • If you want you can use pointers Commented Sep 4, 2013 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<int, int>, or create your own struct if you prefer a more descriptive name. Commented Sep 4, 2013 at 19:55
  • @wudzik, I think pointers in c# is just as shunned as goto statements. Commented Sep 4, 2013 at 19:57
  • This may be relevant stackoverflow.com/questions/2333574/… , but @Jon describes exactly what I would suggest. Commented Sep 4, 2013 at 19:57

3 Answers 3

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
Sign up to request clarification or add additional context in comments.

7 Comments

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<T> - 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.
@JonSkeet: You make a good point. My intention was not to make this code fully-featured -- I would want a Length property and IEnumerable<T> 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.
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: You're the guy who knows what your performance requirements are, not me. Try it and find out!
Or live dangerously and go unsafe (if you use valuetypes) ;p
|
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.

4 Comments

This struct, ArraySegment<>, was extended a lot in .NET 4.5. Before 4.5 it lacked a lot of "natural" functionality.
@JeppeStigNielsen: Ah, that makes a lot of sense. Thanks, will note that.
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: 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.
1

You could use LINQ:

yourArray.Skip(startIndex).Take(numberToTake)

The query is lazily evaluated.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.