Valhalla Legends Forums Archive | .NET Platform | [Reference] [C#] Making Useful CLS-compliant Structures

AuthorMessageTime
Myndfyr
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:

[code]
typedef struct tagRSPVERSION
{
  WORD     MajorVersion;
  WORD     MinorVersion;
  WORD     BuildNumber;
  WORD     RevisionNumber;
} RSP_VERSION, *pRSP_VERSION;
[/code]
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:
[code]
[assembly: CLSCompliant(true)]
[/code]

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.

[code]
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;
}
[/code]

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:

[code]
[MarshalAs(UnmanagedType.U2)]
[/code]

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 :)
April 14, 2005, 1:38 AM
shout
[quote author=Dro link=topic=11265.msg108523#msg108523 date=1113468319]
I don't remember the last time I actually read one of his posts. They are generally bullshit and not worth my time.
[/quote]

[quote author=effect link=topic=11265.msg108520#msg108520 date=1113466593]
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.
[/quote]

Hmmmm....

[quote author=Grok link=topic=10573.msg99834#msg99834 date=1108484541]
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.
[/quote]

Thanks for the info MyndFyre!
April 14, 2005, 3:46 PM

Search