Valhalla Legends Forums Archive | .NET Platform | [c#] ArrayList CopyTo() problem

AuthorMessageTime
mentalCo.
I'm trying to copy my ArrayList into a byte buffer.  For some reason when I try to do this I get this error:

[quote]
An unhandled exception of type 'System.InvalidCastException' occurred in mscorlib.dll

Additional information: At least one element in the source array could not be cast down to the destination array type.
[/quote]

Here's my code...

[code]

byte[] bytSend=new byte[alBuffer.Count + 4];
bytSend[0] = 0xFF;
bytSend[1] = id;

Array.Copy(BitConverter.GetBytes((short)(alBuffer.Count + 4)), 0, bytSend, 2, 2);

alBuffer.CopyTo(0, bytSend, 4, alBuffer.Count);

tcpPRIV.GetStream().Write(bytSend, 0, bytSend.Length);

[/code]
December 20, 2004, 9:23 PM
Myndfyr
I'm not sure if this is because you're using a value type inside of a reference type-list.  If you're using .NET 2.0 Beta 1, I highly recommend using System.Collections.Generics.List<byte> over an ArrayList.

If not, you will likely have to iterate over each item in the ArrayList instead of using .CopyTo(int, byte[], int, int) and manually cast.  This is because when you add a value type to an ArrayList, the framework has to box the value into a reference wrapper.  Apparently, the .CopyTo doesn't try to unbox.  Let's look:

[code]
.method public hidebysig newslot virtual
        instance void  CopyTo(int32 index,
                              class System.Array 'array',
                              int32 arrayIndex,
                              int32 count) cil managed
{
  // Code size      73 (0x49)
  .maxstack  5
  IL_0000:  ldarg.0
  IL_0001:  ldfld      int32 System.Collections.ArrayList::_size
  IL_0006:  ldarg.1
  IL_0007:  sub
  IL_0008:  ldarg.s    count
  IL_000a:  bge.s      IL_001c
  IL_000c:  ldstr      "Argument_InvalidOffLen"
  IL_0011:  call      string System.Environment::GetResourceString(string)
  IL_0016:  newobj    instance void System.ArgumentException::.ctor(string)
  IL_001b:  throw
  IL_001c:  ldarg.2
  IL_001d:  brfalse.s  IL_0038
  IL_001f:  ldarg.2
  IL_0020:  callvirt  instance int32 System.Array::get_Rank()
  IL_0025:  ldc.i4.1
  IL_0026:  beq.s      IL_0038
  IL_0028:  ldstr      "Arg_RankMultiDimNotSupported"
  IL_002d:  call      string System.Environment::GetResourceString(string)
  IL_0032:  newobj    instance void System.ArgumentException::.ctor(string)
  IL_0037:  throw
  IL_0038:  ldarg.0
  IL_0039:  ldfld      object[] System.Collections.ArrayList::_items
  IL_003e:  ldarg.1
  IL_003f:  ldarg.2
  IL_0040:  ldarg.3
  IL_0041:  ldarg.s    count
  IL_0043:  call      void System.Array::Copy(class System.Array,
                                              int32,
                                              class System.Array,
                                              int32,
                                              int32)
  IL_0048:  ret
} // end of method ArrayList::CopyTo
[/code]

Of interest to us is IL_0039 -- it goes to object[]. 

This is from Array.Copy(Array, int, Array, int, int):
[code]
.method public hidebysig static void  Copy(class System.Array sourceArray,
                                          int32 sourceIndex,
                                          class System.Array destinationArray,
                                          int32 destinationIndex,
                                          int32 length) cil managed internalcall
{
} // end of method Array::Copy
[/code]
Note the internalcall flag -- it's part of the CLR, so unfortunately we can't look deeper.

Since we didn't get anything answered, I'll stand by my original assumption that there is an unboxing problem.  You can go through and manually cast each item to byte, but apparently value types don't automatically cast.

BTW, an ArrayList is a bad object to store bytes in.  Figure this:

For every byte you store (one byte of memory), you have to have a four-byte box of it.  There will be 3 bytes also used for padding, since in this case, when storing it as an object you'll be boxing.  Then you've got a four-byte reference to the next node in the List (check out an implementation of a singly-linked list on Google).  So you're using 12 bytes for every one byte you're storing.

A System.Collections.Generic.List<byte> eliminates the boxing wrapper, and you go to 8 bytes per byte to store; not as bad, but still quite a bit.

A System.IO.MemoryStream is a better structure to use.  See this buffer for an alternative.
December 20, 2004, 9:46 PM

Search