• Welcome to Valhalla Legends Archive.
 

[c#] ArrayList CopyTo() problem

Started by mentalCo., December 20, 2004, 03:23 PM

Previous topic - Next topic

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.

Here's my 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);


MyndFyre

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:


.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


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

This is from Array.Copy(Array, int, Array, int, int):

.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

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.
QuoteEvery generation of humans believed it had all the answers it needed, except for a few mysteries they assumed would be solved at any moment. And they all believed their ancestors were simplistic and deluded. What are the odds that you are the first generation of humans who will understand reality?

After 3 years, it's on the horizon.  The new JinxBot, and BN#, the managed Battle.net Client library.

Quote from: chyea on January 16, 2009, 05:05 PM
You've just located global warming.