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.

4

5 回答 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();
    }

}
于 2008-09-11T13:09:40.180 回答
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.

于 2008-09-11T12:48:43.443 回答
1

只需将您的下限存储在一个 const 偏移整数中,然后从您的源作为索引返回的任何值中减去该值。

另外:这是一个旧的 VB6 功能。我认为可能有一个属性可以帮助支持它。

于 2008-09-11T13:32:36.147 回答
0

.NET CLR 区分两种内部数组对象格式:SZ 数组MZ 数组。MZ 数组可以是多维的,并将它们的下限存储在对象中。

造成这种差异的原因有两个:

  1. 一维数组的高效代码生成要求没有下限。有一个下限是非常罕见的。我们不希望在常见情况下为这个很少使用的功能牺牲显着的性能。
  2. 大多数代码需要下限为零的数组。我们当然不想通过检查下限或调整循环边界来污染我们所有的代码。

通过为 SZ 数组创建单独的 CLR 类型来解决这些问题。这是几乎所有实际出现的数组都在使用的类型。

于 2021-09-07T04:56:06.630 回答
0

知道这是老问题,但要充分解释它。

如果类型(在本例中为下界 > 0 的一维数组)不能由类型化代码创建,那么简单的反射类型实例就不能被类型化代码使用。

您注意到的已经在文档中:

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

请注意,从运行时的角度来看,MyArray[] != MyArray[*],但对于多维数组,这两种表示法是等价的。也就是说,Type.GetType("MyArray [,]") == Type.GetType("MyArray[*,*]") 计算结果为真。

在 c#/vb/... 中,您可以将该反射数组保留在对象中,作为对象传递,并仅使用反射来访问它的项目。

-

现在你问“为什么会有 LowerBound?”,好吧,COM 对象不是 .NET,它可以用旧的 VB6 编写,实际上数组对象的 LowerBound 设置为 1(或者任何 VB6 有这样的自由或诅咒,取决于你问谁)。要访问此类对象的第一个元素,您实际上需要使用“comObject(1)”而不是“comObject(0)”。因此,检查下限的原因是当您执行此类对象的枚举以知道从哪里开始枚举时,因为 COM 对象中的元素函数期望第一个元素具有 LowerBound 值,而不是零(0),因此支持是合理的在这种情况下同样的逻辑。想象一下,您在 0 处获取第一个元素的元素值,并使用一些 Com 对象将索引值为 1 甚至索引值为 2001 的元素实例传递给方法,

简而言之:它主要用于遗留支持!

于 2019-08-14T20:11:50.047 回答