Problem casting a byte[] to a class

O

O.B.

I'm trying to do a static_cast at runtime in C# and as I understand it,
"as" is the keyword to use. Unfortunately, the compiler is trying to do
the cast a compilation time. See below.

/* TestA.cs */
namespace TEST {
class TestA {
public ushort val1;
public ushort val2;
public ushort val3;
}
}

/* Main Program */
namespace TEST {
class Program {
static void Main(string[] args) {
TestA test = null;
byte[] testArray = new byte[6];
for (int i = 0; i < testArray.Length; i++) {
testArray = (byte)(i + 1);
}
// Trying to do a C++-style static_cast
// Compilation error:
// Cannot convert type 'byte[]' to 'TEST.TestA'
// via a built-in conversion
test = testArray as TestA;
}
}
}

What am I doing wrong?
 
J

John J. Hughes II

Your assuming the data is sequentially stored and no other data exist in the
class, both assumptions are incorrect.

Normally you can serialize your class and desterilized but if the data needs
to be in a compact byte array which I have found need for several times you
will need to parse the data manually. I think you can overload the
serialization function but I have found the below simpler.

class TestA {
public ushort val1;
public ushort val2;
public ushort val3;

public TestA(byte[] data)
{
val1 = BitConverter.ToUInt16(0);
val2 = BitConverter.ToUInt16(2);
val3 = BitConverter.ToUInt16(4);
}
public ToArray
{
get {
MemoryStream ms = new MemoryStream();
ms.Wirte(BitConverter.GetBytes(val1, 0, 2);
ms.Wirte(BitConverter.GetBytes(val2, 0, 2);
ms.Wirte(BitConverter.GetBytes(val3, 0, 2);
ms.Capacity = ms.Length;
return ms.ToArray();
}
}
}

Regards,
John
 
J

Jon Skeet [C# MVP]

O.B. said:
I'm trying to do a static_cast at runtime in C# and as I understand it,
"as" is the keyword to use. Unfortunately, the compiler is trying to do
the cast a compilation time. See below.

You can't do that in C# (thank goodness, IMO). You should either look
at serialization, or add a method to TestA to convert an array of bytes
(or a stream) into an instance of TestA.
 
J

John J. Hughes II

Oh by the way if you convert to a structure and set the offsets manually you
can then do a memory copy to accomplish what you did in C.

Sort of like this, don't do it as often so a little fuzzer on how.

[StructLayout(LayoutKind.Explicit)]
public struct TestA
{
[FieldOffset(0)] public ushort Val1;
[FieldOffset(2)] public ushort Val2;
[FieldOffset(4)] public ushort Val2;
}

Marshal.Copy(Data, 0, IntPtr <TestA>, 6);

Regards,
John
 
J

Jon Skeet [C# MVP]

John J. Hughes II said:
Your assuming the data is sequentially stored and no other data exist in the
class, both assumptions are incorrect.

Normally you can serialize your class and desterilized but if the data needs
to be in a compact byte array which I have found need for several times you
will need to parse the data manually. I think you can overload the
serialization function but I have found the below simpler.

Note that for your solution, the line

ms.Capacity = ms.Length;

is unnecessary.

Personally, I'd prefer to create the 6 byte array to start with, and
then populate it. The "standard" BitConverter doesn't have any way to
convert into the middle of an existing array, but my own one does :)

See http://www.pobox.com/~skeet/csharp/miscutil and the
EndianBitConverter class.

In this case, the code might be (converting it to a method rather than
a property);

public byte[] ToArray()
{
EndianBitConverter converter = EndianBitConverter.Little;
byte[] ret = new byte[6];
converter.CopyBytes (val1, ret, 0);
converter.CopyBytes (val2, ret, 2);
converter.CopyBytes (val3, ret, 4);
return ret;
}
 
O

O.B.

Actually, the code is bit more complex than what I posted. I'm reading
data off of socket. In C++, I used a static_cast to cast the data to a
union. In C#, I have used StructLayout and FieldOffset to acquire the
"union" behavior. I just need the ability to overlay a structure
(perhaps better than a class for this use case) on top of that byte array.

The problem that I have with "as" when I use a struct is the following
error:
The as operator must be used with a reference type ('TEST.TestA' is a
value type)

Also, I really can't do an assignment on a per attribute basis. The
data received over the socket is in big endian. I prefer to store it in
big-endian and not waste any CPU cycles to convert it to little endian
(speed is key in this application). The implicit and explicit operators
are overridden to query header information from the data received. The
data is then forwarded over many other socket connections in big-endian
format; thus no byte-swapping conversions are required for the majority
of the received data.

Is there no way in C# to map a struct directly on top of a block of memory?


Your assuming the data is sequentially stored and no other data exist in the
class, both assumptions are incorrect.

Normally you can serialize your class and desterilized but if the data needs
to be in a compact byte array which I have found need for several times you
will need to parse the data manually. I think you can overload the
serialization function but I have found the below simpler.

class TestA {
public ushort val1;
public ushort val2;
public ushort val3;

public TestA(byte[] data)
{
val1 = BitConverter.ToUInt16(0);
val2 = BitConverter.ToUInt16(2);
val3 = BitConverter.ToUInt16(4);
}
public ToArray
{
get {
MemoryStream ms = new MemoryStream();
ms.Wirte(BitConverter.GetBytes(val1, 0, 2);
ms.Wirte(BitConverter.GetBytes(val2, 0, 2);
ms.Wirte(BitConverter.GetBytes(val3, 0, 2);
ms.Capacity = ms.Length;
return ms.ToArray();
}
}
}

Regards,
John

O.B. said:
I'm trying to do a static_cast at runtime in C# and as I understand it,
"as" is the keyword to use. Unfortunately, the compiler is trying to do
the cast a compilation time. See below.

/* TestA.cs */
namespace TEST {
class TestA {
public ushort val1;
public ushort val2;
public ushort val3;
}
}

/* Main Program */
namespace TEST {
class Program {
static void Main(string[] args) {
TestA test = null;
byte[] testArray = new byte[6];
for (int i = 0; i < testArray.Length; i++) {
testArray = (byte)(i + 1);
}
// Trying to do a C++-style static_cast
// Compilation error:
// Cannot convert type 'byte[]' to 'TEST.TestA'
// via a built-in conversion
test = testArray as TestA;
}
}
}

What am I doing wrong?

 
O

O.B.

This is *real* close to what I want. The only drawback is that I'm
having to instantiate new memory for the TestA structure before calling
Marshal. The UdpClient is already returning an allocated byte array.
This application has to run as close to real-time as possible.

TestA test;
byte[] testArray = new byte[6];
for (int i = 0; i < testArray.Length; i++) {
testArray = (byte)(i + 1);
}
Marshal.Copy(testArray, 0, (IntPtr)(&test), 6);


Oh by the way if you convert to a structure and set the offsets manually you
can then do a memory copy to accomplish what you did in C.

Sort of like this, don't do it as often so a little fuzzer on how.

[StructLayout(LayoutKind.Explicit)]
public struct TestA
{
[FieldOffset(0)] public ushort Val1;
[FieldOffset(2)] public ushort Val2;
[FieldOffset(4)] public ushort Val2;
}

Marshal.Copy(Data, 0, IntPtr <TestA>, 6);

Regards,
John

O.B. said:
I'm trying to do a static_cast at runtime in C# and as I understand it,
"as" is the keyword to use. Unfortunately, the compiler is trying to do
the cast a compilation time. See below.

/* TestA.cs */
namespace TEST {
class TestA {
public ushort val1;
public ushort val2;
public ushort val3;
}
}

/* Main Program */
namespace TEST {
class Program {
static void Main(string[] args) {
TestA test = null;
byte[] testArray = new byte[6];
for (int i = 0; i < testArray.Length; i++) {
testArray = (byte)(i + 1);
}
// Trying to do a C++-style static_cast
// Compilation error:
// Cannot convert type 'byte[]' to 'TEST.TestA'
// via a built-in conversion
test = testArray as TestA;
}
}
}

What am I doing wrong?

 
J

John J. Hughes II

See my other post with [StructLayout(LayoutKind.Explicit)] and Marshal.Copy.

Regards,
John

O.B. said:
Actually, the code is bit more complex than what I posted. I'm reading
data off of socket. In C++, I used a static_cast to cast the data to a
union. In C#, I have used StructLayout and FieldOffset to acquire the
"union" behavior. I just need the ability to overlay a structure (perhaps
better than a class for this use case) on top of that byte array.

The problem that I have with "as" when I use a struct is the following
error:
The as operator must be used with a reference type ('TEST.TestA' is a
value type)

Also, I really can't do an assignment on a per attribute basis. The data
received over the socket is in big endian. I prefer to store it in
big-endian and not waste any CPU cycles to convert it to little endian
(speed is key in this application). The implicit and explicit operators
are overridden to query header information from the data received. The
data is then forwarded over many other socket connections in big-endian
format; thus no byte-swapping conversions are required for the majority of
the received data.

Is there no way in C# to map a struct directly on top of a block of
memory?


Your assuming the data is sequentially stored and no other data exist in
the class, both assumptions are incorrect.

Normally you can serialize your class and desterilized but if the data
needs to be in a compact byte array which I have found need for several
times you will need to parse the data manually. I think you can overload
the serialization function but I have found the below simpler.

class TestA {
public ushort val1;
public ushort val2;
public ushort val3;

public TestA(byte[] data)
{
val1 = BitConverter.ToUInt16(0);
val2 = BitConverter.ToUInt16(2);
val3 = BitConverter.ToUInt16(4);
}
public ToArray
{
get {
MemoryStream ms = new MemoryStream();
ms.Wirte(BitConverter.GetBytes(val1, 0, 2);
ms.Wirte(BitConverter.GetBytes(val2, 0, 2);
ms.Wirte(BitConverter.GetBytes(val3, 0, 2);
ms.Capacity = ms.Length;
return ms.ToArray();
}
}
}

Regards,
John

O.B. said:
I'm trying to do a static_cast at runtime in C# and as I understand it,
"as" is the keyword to use. Unfortunately, the compiler is trying to do
the cast a compilation time. See below.

/* TestA.cs */
namespace TEST {
class TestA {
public ushort val1;
public ushort val2;
public ushort val3;
}
}

/* Main Program */
namespace TEST {
class Program {
static void Main(string[] args) {
TestA test = null;
byte[] testArray = new byte[6];
for (int i = 0; i < testArray.Length; i++) {
testArray = (byte)(i + 1);
}
// Trying to do a C++-style static_cast
// Compilation error:
// Cannot convert type 'byte[]' to 'TEST.TestA'
// via a built-in conversion
test = testArray as TestA;
}
}
}

What am I doing wrong?

 
J

John J. Hughes II

Jon,

Most of my solutions (problems) have the wrong Endean so I also have a class
for converting. Maybe someday when I have time I will look at yours, you
might have some better ways of handling problems.

Yes you are correct about the ToArray, when I first write the function I was
using the GetBuffer function which was returning the entire capacity. I
later switch to the ToArray function and never removed it, thanks for the
pointer.

Regards,
John

Jon Skeet said:
John J. Hughes II said:
Your assuming the data is sequentially stored and no other data exist in
the
class, both assumptions are incorrect.

Normally you can serialize your class and desterilized but if the data
needs
to be in a compact byte array which I have found need for several times
you
will need to parse the data manually. I think you can overload the
serialization function but I have found the below simpler.

Note that for your solution, the line

ms.Capacity = ms.Length;

is unnecessary.

Personally, I'd prefer to create the 6 byte array to start with, and
then populate it. The "standard" BitConverter doesn't have any way to
convert into the middle of an existing array, but my own one does :)

See http://www.pobox.com/~skeet/csharp/miscutil and the
EndianBitConverter class.

In this case, the code might be (converting it to a method rather than
a property);

public byte[] ToArray()
{
EndianBitConverter converter = EndianBitConverter.Little;
byte[] ret = new byte[6];
converter.CopyBytes (val1, ret, 0);
converter.CopyBytes (val2, ret, 2);
converter.CopyBytes (val3, ret, 4);
return ret;
}
 
T

Tom Spink

O.B. said:
I'm trying to do a static_cast at runtime in C# and as I understand it,
"as" is the keyword to use. Unfortunately, the compiler is trying to do
the cast a compilation time. See below.

/* TestA.cs */
namespace TEST {
class TestA {
public ushort val1;
public ushort val2;
public ushort val3;
}
}

/* Main Program */
namespace TEST {
class Program {
static void Main(string[] args) {
TestA test = null;
byte[] testArray = new byte[6];
for (int i = 0; i < testArray.Length; i++) {
testArray = (byte)(i + 1);
}
// Trying to do a C++-style static_cast
// Compilation error:
// Cannot convert type 'byte[]' to 'TEST.TestA'
// via a built-in conversion
test = testArray as TestA;
}
}
}

What am I doing wrong?


O.B.,

The other guys have explained quite well why you can't do it; and why you
shouldn't do it. You've got to remember C# is a managed programming
language, it doesn't usually have access to raw memory and the same types
of clevery you can do with C++, et al.

However, this is how you do it:

Firstly, define your TestA as a struct, and give it the attribute of
LayoutKind.Sequential:

///
[StructLayout(LayoutKind.Sequential)]
public struct TestA
{
public ushort val1;
public ushort val2;
public ushort val3;
}
///

Then your new test method should read:

///
static void Main( string[] args )
{
TestA test;

byte[] testArray = new byte[6];
for ( int i = 0; i < testArray.Length; i++ )
testArray = (byte)(i + 1);

IntPtr arrayPtr = Marshal.UnsafeAddrOfPinnedArrayElement( testArray, 0 );
test = (TestA) Marshal.PtrToStructure( p, typeof( TestA ) );
}
///

So, the start of the code is the same (loading your array), then the next
two lines perform the magic. The first gets a pointer to the start of the
byte array, and stores it in arrayPtr. The second then generates the
structure, given that pointer and the type of structure being generated.
 
T

Tom Spink

Tom said:
O.B. said:
I'm trying to do a static_cast at runtime in C# and as I understand it,
"as" is the keyword to use. Unfortunately, the compiler is trying to do
the cast a compilation time. See below.

/* TestA.cs */
namespace TEST {
class TestA {
public ushort val1;
public ushort val2;
public ushort val3;
}
}

/* Main Program */
namespace TEST {
class Program {
static void Main(string[] args) {
TestA test = null;
byte[] testArray = new byte[6];
for (int i = 0; i < testArray.Length; i++) {
testArray = (byte)(i + 1);
}
// Trying to do a C++-style static_cast
// Compilation error:
// Cannot convert type 'byte[]' to 'TEST.TestA'
// via a built-in conversion
test = testArray as TestA;
}
}
}

What am I doing wrong?


O.B.,

The other guys have explained quite well why you can't do it; and why you
shouldn't do it. You've got to remember C# is a managed programming
language, it doesn't usually have access to raw memory and the same types
of clevery you can do with C++, et al.

However, this is how you do it:

Firstly, define your TestA as a struct, and give it the attribute of
LayoutKind.Sequential:

///
[StructLayout(LayoutKind.Sequential)]
public struct TestA
{
public ushort val1;
public ushort val2;
public ushort val3;
}
///

Then your new test method should read:

///
static void Main( string[] args )
{
TestA test;

byte[] testArray = new byte[6];
for ( int i = 0; i < testArray.Length; i++ )
testArray = (byte)(i + 1);

IntPtr arrayPtr = Marshal.UnsafeAddrOfPinnedArrayElement( testArray, 0
); test = (TestA) Marshal.PtrToStructure( p, typeof( TestA ) );
}
///

So, the start of the code is the same (loading your array), then the next
two lines perform the magic. The first gets a pointer to the start of the
byte array, and stores it in arrayPtr. The second then generates the
structure, given that pointer and the type of structure being generated.


I made a slight error while I was touch-typing from monitor 1, to monitor 2:

On the line of code that is PtrToStructure, I left the first parameter as p,
when in fact it should be arrayPtr. Appologies.
 
J

John J. Hughes II

I don't understand why you are coping the data from one array to another,
just use the array the UdpClient is giving you.

Personally I normally use socket and have found the socket can not give you
data as fast as you can process it so you will be spending more time idle
anyway so you might be trying to optimize something more then needed.

Regards,
John

O.B. said:
This is *real* close to what I want. The only drawback is that I'm having
to instantiate new memory for the TestA structure before calling Marshal.
The UdpClient is already returning an allocated byte array. This
application has to run as close to real-time as possible.

TestA test;
byte[] testArray = new byte[6];
for (int i = 0; i < testArray.Length; i++) {
testArray = (byte)(i + 1);
}
Marshal.Copy(testArray, 0, (IntPtr)(&test), 6);


Oh by the way if you convert to a structure and set the offsets manually
you can then do a memory copy to accomplish what you did in C.

Sort of like this, don't do it as often so a little fuzzer on how.

[StructLayout(LayoutKind.Explicit)]
public struct TestA
{
[FieldOffset(0)] public ushort Val1;
[FieldOffset(2)] public ushort Val2;
[FieldOffset(4)] public ushort Val2;
}

Marshal.Copy(Data, 0, IntPtr <TestA>, 6);

Regards,
John

O.B. said:
I'm trying to do a static_cast at runtime in C# and as I understand it,
"as" is the keyword to use. Unfortunately, the compiler is trying to do
the cast a compilation time. See below.

/* TestA.cs */
namespace TEST {
class TestA {
public ushort val1;
public ushort val2;
public ushort val3;
}
}

/* Main Program */
namespace TEST {
class Program {
static void Main(string[] args) {
TestA test = null;
byte[] testArray = new byte[6];
for (int i = 0; i < testArray.Length; i++) {
testArray = (byte)(i + 1);
}
// Trying to do a C++-style static_cast
// Compilation error:
// Cannot convert type 'byte[]' to 'TEST.TestA'
// via a built-in conversion
test = testArray as TestA;
}
}
}

What am I doing wrong?

 
B

Ben Voigt

O.B. said:
This is *real* close to what I want. The only drawback is that I'm having
to instantiate new memory for the TestA structure before calling Marshal.
The UdpClient is already returning an allocated byte array. This
application has to run as close to real-time as possible.

This whole business breaks all type-safety rules (only the BitConverter
method was type-safe).

If that's ok with you, and it sounds like it is, then use explicit layout
and pointers inside an unsafe block.

TestA test;
byte[] testArray = new byte[6];
for (int i = 0; i < testArray.Length; i++) {
testArray = (byte)(i + 1);
}
Marshal.Copy(testArray, 0, (IntPtr)(&test), 6);


Oh by the way if you convert to a structure and set the offsets manually
you can then do a memory copy to accomplish what you did in C.

Sort of like this, don't do it as often so a little fuzzer on how.

[StructLayout(LayoutKind.Explicit)]
public struct TestA
{
[FieldOffset(0)] public ushort Val1;
[FieldOffset(2)] public ushort Val2;
[FieldOffset(4)] public ushort Val2;
}

Marshal.Copy(Data, 0, IntPtr <TestA>, 6);

Regards,
John

O.B. said:
I'm trying to do a static_cast at runtime in C# and as I understand it,
"as" is the keyword to use. Unfortunately, the compiler is trying to do
the cast a compilation time. See below.

/* TestA.cs */
namespace TEST {
class TestA {
public ushort val1;
public ushort val2;
public ushort val3;
}
}

/* Main Program */
namespace TEST {
class Program {
static void Main(string[] args) {
TestA test = null;
byte[] testArray = new byte[6];
for (int i = 0; i < testArray.Length; i++) {
testArray = (byte)(i + 1);
}
// Trying to do a C++-style static_cast
// Compilation error:
// Cannot convert type 'byte[]' to 'TEST.TestA'
// via a built-in conversion
test = testArray as TestA;
}
}
}

What am I doing wrong?

 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top