13

Although perhaps a bizare thing to want to do, I need to create an Array in .Net with a lower bound > 0. This at first seems to be possible, using:

Array.CreateInstance(typeof(Object), new int[] {2}, new int[] {9});

Produces the desired results (an array of objects with a lower bound set to 9). However the created array instance can no longer be passed to other methods expecting Object[] giving me an error saying that:

System.Object[*] can not be cast into a System.Object[]. What is this difference in array types and how can I overcome this?

Edit: test code =

Object x = Array.CreateInstance(typeof(Object), new int[] {2}, new int[] {9});
Object[] y = (Object[])x;

Which fails with: "Unable to cast object of type 'System.Object[*]' to type 'System.Object[]'."

I would also like to note that this approach DOES work when using multiple dimensions:

Object x = Array.CreateInstance(typeof(Object), new int[] {2,2}, new int[] {9,9});
Object[,] y = (Object[,])x;

Which works fine.

5 Answers 5

3

The reason why you can't cast from one to the other is that this is evil.

Lets say you create an array of object[5..9] and you pass it to a function F as an object[].

How would the function knows that this is a 5..9 ? F is expecting a general array but it's getting a constrained one. You could say it's possible for it to know, but this is still unexpected and people don't want to make all sort of boundary checks everytime they want to use a simple array.

An array is the simplest structure in programming, making it too complicated makes it unsusable. You probably need another structure.

What you chould do is a class that is a constrained collection that mimics the behaviour you want. That way, all users of that class will know what to expect.

class ConstrainedArray<T> : IEnumerable<T> where T : new()
{
    public ConstrainedArray(int min, int max)
    {
        array = new T[max - min];
    }

    public T this [int index]
    {
        get { return array[index - Min]; }
        set { array[index - Min] = value; }
    }

    public int Min {get; private set;}
    public int Max {get; private set;}

    T[] array;

    public IEnumerator<T> GetEnumerator()
    {
        return array.GetEnumarator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return array.GetEnumarator();
    }

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

3 Comments

Thanks for the code snippet, but I wish to avoid any type of wrapping when necessary, as this problem arises from a VB6 migration issue, where large amounts of code expect lower bounds > 0. Therefore I am doing my best to determine first if it is possible to NOT use a custom class.
While your explanations about the specific case make sense, I do not quite agree with your wording. Having moved to .NET from a Pascal background, a "general array" or "simple array" has always been something with an arbitrary start index to me. C# sees that differently, but that can certainly not serve as an excuse for omitting "all sort of boundary checks" when working with a multi-language platform like .NET. After all, the boundary check for the length of the array (implicitly, the upper bound of allowable index values) needs to be there in C# as well. Good wrapper class, nonetheless.
GetEnumerator has a typo: GetEnumarator
1

I'm not sure about why that can't be passed as Object[], but wouldn't be easy if you just create a real class to wrap an array and handle your "weird logic" in there?

You'd get the benefits of using a real reference object were you could add "intelligence" to your class.

Edit: How are you casting your Array, could you post some more code? Thanks.

1 Comment

It would be nice to wrap, but for certain reasons and in certain circumstances it needs to be avoided. I will edit with more code.
1

Just store your lower bound in a const offset integer, and subtract that value from whatever your source returns as the index.

Also: this is an old VB6 feature. I think there might be an attribute to help support it.

Comments

1

Know it's old question, but to fully explain it.

If type (in this case a single-dimension array with lower bound > 0) can't be created by typed code, simply reflected type instance can't be consumed by typed code then.

What you have noticed is already in documentation:

https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names

Note that from a runtime point of view, MyArray[] != MyArray[*], but for multidimensional arrays, the two notations are equivalent. That is, Type.GetType("MyArray [,]") == Type.GetType("MyArray[*,*]") evaluates to true.

In c#/vb/... you can keep that reflected array in object, pass around as object, and use only reflection to access it's items.

-

Now you ask "why is there LowerBound at all?", well COM object aren't .NET, it could be written in old VB6 that actually had array object that has LowerBound set to 1 (or anything VB6 had such freedom or curse, depends whom you ask). To access first element of such object you would actually need to use 'comObject(1)' instead of 'comObject(0)'. So the reason to check lower bound is when you are performing enumeration of such object to know where to start enumeration, since element functions in COM object expects first element to be of LowerBound value, and not Zero (0), it was reasonable to support same logic on such instances. Imagine your get element value of first element at 0, and use some Com object to pass such element instance with index value of 1 or even with index value of 2001 to a method, code would be very confusing.

To put it simply: it's mostly for legacy support only!

Comments

1

The .NET CLR differentiates between two internal array object formats: SZ arrays and MZ arrays. MZ arrays can be multi-dimensional and store their lower bounds in the object.

The reason for this difference is two-fold:

  1. Efficient code generation for single-dimensional arrays requires that there is no lower bound. Having a lower bound is incredibly uncommon. We would not want to sacrifice significant performance in the common case for this rarely used feature.
  2. Most code expects arrays with zero lower bound. We certainly don't want to pollute all of our code with checking the lower bound or adjusting loop bounds.

These concerns are solved by making a separate CLR type for SZ arrays. This is the type that almost all practically occurring arrays are using.

1 Comment

But how do I pass a Object[*] or Object[9..11] around? Results in a compile error if I try to declare it, and Array is a bit too unspecific...

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.