7

When I Tried to return an Array in VFP9 language COM/DLL to my .NET C# project I receive a System.Object[*] array and I can not cast to System.Object[] (Without asterisk).

Marius Bancila
  • 16,053
  • 9
  • 49
  • 91
Cheva
  • 331
  • 5
  • 12
  • What is an `Object[*]`? Do you mean an `Object*` as in an unmanaged pointer to an Object array? – Callum Rogers Sep 16 '10 at 22:24
  • 2
    @Callum: No, it is a System.Array with a non-zero starting index. – Timwi Sep 16 '10 at 22:27
  • Yes @Callum, so How I can work with this? – Cheva Sep 16 '10 at 22:31
  • @Timwi: Yikes, you can have arrays (as in real `System.Array` s) that don't start at zero? As in they would give an `IndexOutOfRangeException` if you tried to access the zeroth element? How is this useful? – Callum Rogers Sep 16 '10 at 22:33
  • @Cheva: So it is a pointer problem then... do you know the size of the array or is it null terminated? – Callum Rogers Sep 16 '10 at 22:36
  • @Callum: Yes, although the only way to access such an array in C# is to use `Array.GetValue()`, and yes, you would get the `IndexOutOfRangeException`. The reason the CLR supports such arrays is because of VB.NET. – Timwi Sep 16 '10 at 22:39
  • (Also, I doubt Cheva knew what they were saying when they answered your question with “yes”. I don’t think it’s a pointer problem.) – Timwi Sep 16 '10 at 22:41
  • @Timwi: That's why I haven't answered. I'm guessing this VFP language uses arrays starting at 1 or some other value. – Callum Rogers Sep 16 '10 at 22:42
  • @Callum Rogers, hopefully it isn't "some other value"... An array that starts at 42 would be really weird ;) (although you can create such an array in C#...) – Thomas Levesque Sep 16 '10 at 22:52
  • (I have once had a reasonable usecase for an array that starts at −1.) – Timwi Sep 16 '10 at 23:08

3 Answers3

9

Timwi's solution should work fine. You can do something a bit simpler using Linq:

object[] newArray = sourceArray.Cast<object>().ToArray();

In case you need to recreate a System.Object[*] to pass it back to VFP, you can use this overload of the Array.CreateInstance method:

public static Array CreateInstance(
    Type elementType,
    int[] lengths,
    int[] lowerBounds
)

You can use it as follows:

object[] normalArray = ...

// create array with lower bound of 1
Array arrayStartingAt1 =
    Array.CreateInstance(
        typeof(object),
        new[] { normalArray.Length },
        new[] { 1 });

Array.Copy(normalArray, 0, arrayStartingAt1, 1, normalArray.Length);
Rand Random
  • 7,300
  • 10
  • 40
  • 88
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • Somehow I never think of the solutions which are shorter but much slower. I realise that in most cases the performance penalty is insignificant... – Timwi Sep 16 '10 at 22:35
  • Does this work because the `Cast` can simply iterate over the array using an IEnumerator instead of worrying where the array starts? If so couldn't you just use `Select(x => x).ToArray()` (or even just`ToArray()` ) instead? It would remove the need for a cast from `object` to `object`. – Callum Rogers Sep 16 '10 at 22:47
  • Indeed, it will be a bit slower. When the source sequence implements `ICollection`, `ToArray` takes advantage of that, but arrays with non-zero bounds only implements the non-generic `ICollection`... – Thomas Levesque Sep 16 '10 at 22:48
  • @Callum Rogers, yes, exactly. `Array` implements `IEnumerable` – Thomas Levesque Sep 16 '10 at 22:49
  • You MASTER!. Thank you very much! – Cheva Sep 16 '10 at 22:49
  • @Myself: You can't just use `ToArray()` or `Select()` because `Array` only implements the non-generic `IEnumerable` and they both require `IEnumerable`. +1 for this answer for cleverness. – Callum Rogers Sep 16 '10 at 23:01
  • What happens if I have a method like `public int ReadFPGA( out object[*] param, ...)` in a dll, where the run-time version is 4.0.x? The project and all other dll is 4.5.2., only this one is 4.0.x. – minus one Oct 17 '17 at 15:18
6

Unfortunately, you cannot cast it directly. You can, however, create a new array of type object[] and copy the data over. Something like...

Array sourceArray = ...;

if (sourceArray.Rank != 1)
    throw new InvalidOperationException("Expected a single-rank array.");

object[] newArray = new object[sourceArray.Length];
Array.Copy(sourceArray, sourceArray.GetLowerBound(0),
           newArray, 0, sourceArray.Length);
Timwi
  • 65,159
  • 33
  • 165
  • 230
  • Great, this works, but if you have thousands of items, you think? 'll Keep testing with queries or grades. – Cheva Sep 16 '10 at 22:48
  • googling I find a VFP instruction for this from VFP source code: the function ComArray(This.newArray, 10) that pass to based zero. the inverse method – Cheva Sep 20 '10 at 19:53
5

I had a similar issue. Got an array as a dynamic object from an interop assembly, also starting from index one. When I tried to convert this into an Array object, I got the same error message.
Doing as the other answers suggest did not work. For a strange reason, even reading the Length property raised the exception.
I found this answer, and it worked.
Apparently, if you use C# 4.0, you have to cast the dynamic to object first, then you can convert it to Array. In prior versions of .NET you can cast directly.
Here is an Explanation why.

RoadBump
  • 733
  • 7
  • 16