5

C# allows creating and populating multidimensional arrays, here is a simple example:

    public static void Main(String[] args)
    {
        var arr = (int[,])CreateArray(new [] {2, 3}, 8);
        Console.WriteLine("Value: " + arr[0,0]);
    }

    // Creates a multidimensional array with the given dimensions, and assigns the
    // given x to the first array element
    public static Array CreateArray<T>(int[] dimLengths, T x)
    {
        var arr = Array.CreateInstance(typeof(T), dimLengths);
        var indices = new int[dimLengths.Length];
        for (var i = 0; i < indices.Length; i++)
            indices[i] = 0;
        arr.SetValue(x, indices);  // Does boxing/unboxing
        return arr;
    }

This works well. However, for some reason there is no generic version of Array.SetValue(), so the code above does boxing/unboxing, which I'd like to avoid. I was wondering if I missed something or if this is an omission in the .NET API?

6
  • Arrays have an incompatible type of genericity. Commented Nov 22, 2014 at 9:52
  • @HansPassant, how does the problematic covariance of arrays? Why doesn't it prevent the existence of a generic SetValue() method? Commented Nov 22, 2014 at 10:07
  • The required runtime type check can't be bypassed, the point of generics is that these checks are performed at compile time. The generics plumbing is missing the infrastructure to do it at runtime. Commented Nov 22, 2014 at 10:19
  • The for loop is unnecessary. A new array is guaranteed to be initialized upon creation. So new int[L] makes an array filled with default(int) which is 0. Commented Nov 22, 2014 at 10:46
  • You have arrays whose ranks are not known at compile-time, and there is no way around the boxing. But I wonder what happens in the more usual case where the C# compiler knows the rank and the element type of the array. So if I write the code int[,] arr = new int[2, 3]; arr[0, 0] = 8;, will the value 8 be boxed? I guess not. Will check next time I come by a C# compiler, from the resulting IL. Commented Nov 22, 2014 at 10:57

2 Answers 2

1

No, you are not missing anything: Arrays does not have an option that sets the value without boxing and unboxing.

You do have an alternative to this with LINQ, but it is probably going to be slower than boxing/unboxing for a single element, because compiling a dynamic lambda would "eat up" the potential benefits:

public static Array CreateArray<T>(int[] dimLengths, T x) {
    var arr = Array.CreateInstance(typeof(T), dimLengths);

    var p = Expression.Parameter(typeof(object), "arr");
    var ind = new Expression[dimLengths.Length];
    for (var i = 0; i < dimLengths.Length; i++) {
        ind[i] = Expression.Constant(0);
    }
    var v = Expression.Variable(arr.GetType(), "cast");
    var block = Expression.Block(
        new[] {v}
    ,   new Expression[] {
            Expression.Assign(v, Expression.Convert(p, arr.GetType()))
        ,   Expression.Assign(Expression.ArrayAccess(v, ind), Expression.Constant(x))
        ,   Expression.Constant(null, typeof(object))
        }
    );
    Expression.Lambda<Func<object, object>>(block, p).Compile()(arr);
    return arr;
}

If you wanted to set all elements in a loop, you could modify the above to compile a dynamically created lambda with multiple nested loops. In this case, you could get an improvement on having to perform multiple boxing and unboxing in a series of nested loops.

for some reason there is no generic version of Array.SetValue()

While it is definitely possible to write a generic method similar to SetValue in the Array class, it may not be desirable. A generic method on a non-generic class would give a false promise of compile-time type safety, which cannot be guaranteed, because the compiler does not know the runtime type of the Array object.

Sign up to request clarification or add additional context in comments.

1 Comment

The code generation approach is interesting, and if a great deal of elements are used it could indeed be worthwhile (compiling the lambda is expensive but occurs only once). As you say, LINQ is not an option because of performance...
0

I didn't find any generic ways either to set a value into an Array instance, so I guess the only workaround is to use the unsafe context to avoid boxing.

However, there can be no generic version, now when I think of it. See, when you define a generic method method<T>()..., you do define the parameter for the method: ...<T>(T[] a)... where you have to be specific about the dimensions count, which is one. To create a twodimensional parameter, you define it like this ...<T>(T[,] a)... and so on.

As you can see, by the current syntax of C#, you simple cannot create a generic method, which can accept any-dimensional array.

3 Comments

I don't follow why there can't be a generic version; a non-generic SetValue() method does exist, accepting an object and an array of indices. Why can there not be a SetValue<T>(T, Int32[]) that does the same thing? Note I'm not trying to create a generic method that accepts an array of an unknown dimension, I'd only like to have a generic SetValue() overload.
@Shay I just told you why. Try creating your own method which looks like you think it has to and you will understand.
I'm well aware of the fact that I can't write a generic method that accepts an array of unknown dimensions. What I'm trying to understand is why the .NET API doesn't provide a generic SetValue<T>(T, Int32[]), which corresponds exactly to the non-generic version that already exists. In other words, I'm trying to understand whether there's some specific reason why Microsoft can't provide this?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.