6

My question is similar to Getting a sub-array from an existing array although a little different notion is very important in my case - I can't use memory copying.

Let's say I have array X of 10000 elements, I need array Y that would contains 9000 elements from X, starting from X's index 500.

But I don't want to copy part of X to new array Y, so I don't want to use Array.Copy, Array.Clone, System.Block.Copy, IEnumerables etc. I want Y to be reference to X - Y[0] would be in fact X[500], Y[1] corresponds to X[501], ..., Y[9000] is X[9500].

Thus, for example changing value of X[100] would at the same time change value of Y[600]. How can I achieve this in C#?

Community
  • 1
  • 1
xcoder37
  • 499
  • 6
  • 21
  • Of course, you can use `unsafe` operations (like to C++ pointers), but it's not recommended. – General-Doomer Jun 01 '15 at 10:22
  • 2
    Have you considered making a simple wrapper that acts like an array? It wouldn't *be* an array, but if you wrote your code to use the wrapper instead you could create wrappers for smaller slices of the underlying bigger array. – Lasse V. Karlsen Jun 01 '15 at 10:23
  • *Why* can't you use memory copying? Is there a reason you can't pass the offset around (perhaps as part of a wrapper, like @LasseV.Karlsen suggests)? – Rowland Shaw Jun 01 '15 at 10:26
  • With copying he would have 2 distinct arrays + the overhead of making the copy, both of which could be unwanted (he doesn't say why, that's just my own take on the problem) – Lasse V. Karlsen Jun 01 '15 at 10:26
  • If you can't copy elements, then perhaps initial choice to use array as a storage was wrong? Depends on how you going to use it you can implement `IEnumerable` while having inside just a List to hold/add/remove items. – Sinatr Jun 01 '15 at 10:28

3 Answers3

7

You could wrap it in another object with something like this:

class View<T>
{
     private T[] _array;
     private long _start;
     private long _length;
     public View(T[] array, long start, long length) { ... }
     public T this[long index] 
     {
         get 
         {
             if (/*do bounds check here*/) 
             {
                 return _array[_start + index];
             }    
         }
     }
}

This won't be an array, but a projection of one.

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
  • 1
    I don't think array indexes can be longs, only ints (in number that is), and I would make the view a struct since it will effectively hold 2 32-bit values + a memory reference, make it even cheaper. Additionally I would make the fields readonly, but other than that, this is exactly the solution I would pick myself. – Lasse V. Karlsen Jun 01 '15 at 10:28
  • Thank you, looks very promising :) – xcoder37 Jun 01 '15 at 10:31
  • 1
    @xcoder37 For completeness, add support for `IEnumerable` and `IList` as well. – Lasse V. Karlsen Jun 01 '15 at 10:31
  • @xcoder37 even though it doesn't support indexers, `ArraySegment` has a similar concept. https://msdn.microsoft.com/en-us/library/1hsbd92d%28v=vs.110%29.aspx – Daniel A. White Jun 01 '15 at 10:32
3

You can use an ArraySegment. Here's an example:

String[] X = { "one", "two", "three", "four", "five"};

ArraySegment<String> arraySegment = new ArraySegment<String>(X, 1,3); // will contain {"two", "three", "four"}
arraySegment.Array[arraySegment.Offset + 1] = "3"; // X will contain { "one", "two", "3", "four", "five"};
                             // and arraySegment.Array will contain {"two", "3", "four"}
Nasreddine
  • 36,610
  • 17
  • 75
  • 94
0

Sadly ArraySegment<T> is sealed, else you could easily have extended it with the proper array-syntax, ie indexers and the likes.

If I were you I would go with ArraySegment<T> and if it doesn't have the proper requirements, like ElementAt(n) being to inefficient, just implement a better implementation. Ex:

public static class ArrayExtensions 
{
  // Getter using underlying array
  public static T GetValueAt<T>(this ArraySegment<T> array, int index)
  { // No safe checks here, would recommend them in production though
    return array.Array[array.Offset + index];
  }
  // Setter using underlying array
  public static void SetValueAt<T>(this ArraySegment<T> array, int index, T value)
  { // maybe we should check that the calculated index is valid? Or just blow up?
    array.Array[array.Offset + index] = value;
  }
}
flindeberg
  • 4,887
  • 1
  • 24
  • 37