Ken Allen wrote:
I have some code from C/C++ that I am attempting to port to C#. I have come
across an interesting problem that is quite common in complex C/C++ code:
the us of UNION in structure definitions to permit the same piece of memory
to be referenced as different data types. This is often used to save space
and permit a single piece of memory to contain different types of data,
typically based on some other flag in the structure. In other cases this
simply permits the data to be viewed in different ways -- for example, as an
array of 16 32-bit integers or as an array of 64 bytes. Since this is
defined at compile time, there is no runtime overhead in performing any
conversions or the like.
How can I achieve the same thing in C#? I am more interested in the latter
situation right now -- the ability to view the same piece of memory as
either an array of 16 "int" or 74 "byte" objects.
Here's an implementation of a Union class that you can pass a byte array
into via the constructor. You can then access it as bytes, Int16's or
Int32's via a few properties.
This might do what you need, or at least give you a starting point.
Sorry for the lack of comments this was just a quickly I did sometime
over the weekend:
//================================================== =================
using System;
public class ByteIndexer {
private byte[] _data;
public ByteIndexer( byte [] data) {
_data = data;
}
public byte this[int index] {
get {
return( _data[index]);
}
set {
_data[index] = value;
}
}
}
public class Int16Indexer {
private byte[] _data;
private const int _elementSize = 2;
public Int16Indexer( byte [] data) {
if ((data.Length % _elementSize) != 0) {
throw new ArgumentException( String.Format( "Size of a
Int16Indexer array must be a multiple of {0}", _elementSize));
}
_data = data;
}
public short this[int index] {
get {
if ((index < 0) || ((index+1) * _elementSize >
_data.Length)) {
throw new IndexOutOfRangeException();
}
return( BitConverter.ToInt16( _data, index *
_elementSize));
}
set {
if ((index < 0) || ((index+1) * _elementSize >
_data.Length)) {
throw new IndexOutOfRangeException();
}
Array.Copy( BitConverter.GetBytes( value), 0, _data,
index * _elementSize, _elementSize);
}
}
}
public class Int32Indexer {
private byte[] _data;
private const int _elementSize = 4;
public Int32Indexer( byte [] data) {
if ((data.Length % _elementSize) != 0) {
throw new ArgumentException( String.Format( "Size of a
Int32Indexer array must be a multiple of {0}", _elementSize));
}
_data = data;
}
public int this[int index] {
get {
if ((index < 0) || ((index+1) * _elementSize >
_data.Length)) {
throw new IndexOutOfRangeException();
}
return( BitConverter.ToInt32( _data, index *
_elementSize));
}
set {
if ((index < 0) || ((index+1) * _elementSize >
_data.Length)) {
throw new IndexOutOfRangeException();
}
Array.Copy( BitConverter.GetBytes( value), 0, _data,
index * _elementSize, _elementSize);
}
}
}
public class Union {
private byte[] _data;
private const int _defaultSize = 16;
private const int _elementSize = 4;
private ByteIndexer _byteIndexer;
private Int16Indexer _int16Indexer;
private Int32Indexer _int32Indexer;
private void initMembers( byte [] data) {
_byteIndexer = new ByteIndexer( data);
_int16Indexer = new Int16Indexer( data);
_int32Indexer = new Int32Indexer( data);
}
public Union(): this( _defaultSize) {}
public Union( int size) {
// size must be a multiple of 4 to cleanly handle ints
if ((size % _elementSize) != 0) {
throw new ArgumentOutOfRangeException( "size", size,
String.Format( "Size of a Union must be a multiple of {0}", _elementSize));
}
_data = new byte[size];
initMembers( _data);
}
public Union( byte[] data) {
if ((data.Length % _elementSize) != 0) {
throw new ArgumentException( String.Format( "Size of a
Union must be a multiple of {0}", _elementSize));
}
initMembers( data);
}
public byte[] GetArray() {
return( _data); //TODO: should a copy of the array
be returned instead of a reference?
}
public ByteIndexer ByteAccess {
get {
return( _byteIndexer);
}
}
public Int16Indexer Int16Access {
get {
return( _int16Indexer);
}
}
public Int32Indexer Int32Access {
get {
return( _int32Indexer);
}
}
}
/// <summary>
/// Summary description for Class1.
/// </summary>
class MainClass
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
int i;
Union myUnion = new Union( 32);
for (i = 0; i < 32; i++) {
myUnion.ByteAccess[i] = (byte) i;
}
myUnion.Int32Access[3] = unchecked( (int) 0xdeadbeef);
Console.WriteLine( "DWord access...");
Console.WriteLine();
for (i = 0; i < 32/4; i++) {
Console.WriteLine( myUnion.Int32Access[i].ToString( "X"));
}
// now Word sized access
Console.WriteLine( "Word access...");
Console.WriteLine();
Console.WriteLine( myUnion.Int16Access[15].ToString( "X"));
try {
myUnion.Int16Access[16] = 255;
}
catch (Exception ex) {
// the above should throw an exception
Console.WriteLine( ex);
}
}
}
//================================================== =================
--
mikeb