• Welcome to Valhalla Legends Archive.
 

[Reference] [C#] Making Useful CLS-compliant Structures

Started by MyndFyre, April 13, 2005, 08:38 PM

Previous topic - Next topic

MyndFyre

The title is a little misleading.  This is intended as a reference article that allows you to create structures in C# that can use unsigned types and still work in CLSCompliant assemblies.

This need came up in a project I'm working on when I had defined a version structure in C terms:


typedef struct tagRSPVERSION
{
  WORD     MajorVersion;
  WORD     MinorVersion;
  WORD     BuildNumber;
  WORD     RevisionNumber;
} RSP_VERSION, *pRSP_VERSION;

WORD is of course an unsigned 16-bit value.  Its compared type is System.UInt16, or C#'s ushort.

My project is also intended to be able to be used in languages that do not support unsigned types, which are not CLS-compliant.  To that end, the assembly is marked with the attribute:

[assembly: CLSCompliant(true)]


In order to make this type CLS compliant, have the version values readable in other languages, and still maintain it to use unsigned values, you can lay out your structure explicitly.


using System;
// necessary to get some attributes
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Explicit)]
public struct RspVersion
{
  [FieldOffset(0)]
  public short MajorVersion;
  [FieldOffset(0), CLSCompliant(false)]
  public ushort Major;

  [FieldOffset(2)]
  public short MinorVersion;
  [FieldOffset(2), CLSCompliant(false)]
  public ushort Minor;

  [FieldOffset(4)]
  public short BuildNumber;
  [FieldOffset(4), CLSCompliant(false)]
  public ushort Build;

  [FieldOffset(6)]
  public short RevisionNumber;
  [FieldOffset(6), CLSCompliant(false)]
  public ushort Revision;
}


Of course you'll see overflow problems crop up if you use a language that does not support unsigned integers.  To counteract this problem, you can specify that field offsets increase by 4, and the CLS-compliant fields use the int type (System.Int32) instead of the short type.  This can, however, have unintended consequences with invalid assignments to the int field.  This can be fixed through wrapping it with a property.  If you choose to use the Int32 type, I also recommend you apply the attribute:


[MarshalAs(UnmanagedType.U2)]


so only relevant data is passed through.

Also, I recommend that (at least for this type), you implement the IComparable interface, so that any comparisons are done transparent to outside users (so you can check the actual data in the unsigned values).  Further practice would also have you override .Equals(), .GetHashCode(), .ToString(), and the operators ==, <, and >.

Hope this helps someone out down the line :)
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.

shout

Quote from: Dro on April 14, 2005, 03:45 AM
I don't remember the last time I actually read one of his posts. They are generally bullshit and not worth my time.

Quote from: effect on April 14, 2005, 03:16 AM
Generally, i just skip your posts, as im pretty sure most people do, because your only almost always trying to fly your own flag.

Hope this helps you get a life.

Hmmmm....

Quote from: Grok on February 15, 2005, 10:22 AM
If you have a technical answer, please jump in and provide it.  If you ALSO have an opinion, nothing wrong with including that.  If you ONLY have an opinion that is not directly related to solving the problem the person asked, move on to the next post, or place your opinion in an opinions forum.

Thanks for the info MyndFyre!