Freestyle Coding

Programming in the Real World

  Home  |   Contact  |   Syndication    |   Login
  55 Posts | 0 Stories | 41 Comments | 0 Trackbacks

News

Tag Cloud


Archives

Post Categories

Charity

Conferences

Professional

Projects

Social Networks

I ran into an annoying problem while wrapping a native library in managed code. The original library was VERY native. The included header files included hundreds of typedefs and more than a few structures with unions. Now, after this little experiment, I could (and, more than likely, will later) speak volumes about Marshaling memory between managed and native code.

However, as I said above, this post is dedicated to an annoying issue. Those of you familiar with C++ already know this, but I explain for those whom are unfamiliar. In C++, you can "create" a new data type by using a typedef statement. This creates a symbolic link between the original type, structure or class and the new name. Unlike a #define, this is done at compile time, so you get as close to strong type checking as C++ is capable.

So, this leads to problems in C#. There is no good way to do this. The closest built in mechanism is the alias feature that you use through the using directive.

using NewInt = System.Int32;
This, however, has a few issues. The first issue is that the using directive only has a scope of the current file. Thus, you have to copy these directives to every file in your library. To give some credit to this behavior, it keeps the underlying types. Thus, you can have your symbolic names in your library, but the consumer of the library will still see the raw data types.

The other issue is that it reeks havoc with intellisense. The problem is that you have aliased the original type to the new name. This is done on a global level for the file. Imagine you have more than one alias for the same type.

using NewInt1 = System.Int32;
using NewInt2 = System.Int32;
Now, every System.Int32 in the file will report itself as a NewInt2. While this seems annoying but manageable, you're not thinking of the big picture. If you have 50 of these in the same file, you probably don't know which one is which.

The ideal case is to create a strongly typed new data type with the correct name. If we were dealing with classes, we could just base on the original. The most classic example if this is creating your own Exception.

public class NewException : System.Exception {
public NewException() : base() {}
public NewException( string message ) : base( message ) {}
public NewException( string message, Exception innerException ) : base( message, innerException ) {}
public NewException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context
) : base( info, context ) {}
}

Unfortunately, you can't base off a structure. If you know the difference between a class and a structure, skip the rest of this and the next paragraph. If you're still here, every class is a reference type. This means, under the covers, when you create the object, the runtime allocates memory on the heap and creates a pointer to the object. Then, you just pass the pointer around. This is why there can be a default Object.Equals. The base class just checks to see if the two objects point to the same place in memory.

On the other hand, data types and structures are value types. This means, under the covers, when you create the structure, the runtime allocates memory on the stack and copies the values into memory. Then, when you pass the structure around, the system creates a new allocation on the stack and copies the values over. This is why you have to specify ref when you want to change the value of the structure inside a function. Just to be complete, the default Object.Equals checks each field in the structure and does a value compare.

Since you can't base off value types, you can't create these nice, global, type safe references. However, after a little brain work and a lot of profanity, I came to a solution.

public struct NewInt3 {
private Int32 VALUE;
public static implicit operator Int32( NewInt3 value ) {
return value.VALUE;
}
public static implicit operator NewInt3( Int32 value ) {
NewInt3 _ReturnValue = new NewInt3();
_ReturnValue.VALUE = value;
return _ReturnValue;
}
}

This allows the symbolic name to work across everything. For the most part, it will behave exactly like a data type. Of course, if you run into some operator or cast that is not working as expected, just add your new operator.

I can only hope this saves you the brain damage I endured...

posted on Thursday, December 13, 2012 10:11 AM

Feedback

# re: Creating data types in C# 6/27/2013 8:12 AM hrishikesh shivacharan
thx.. for helping

# re: Creating data types in C# 7/10/2013 1:43 AM hrishikesh
nice one

Post A Comment
Title:
Name:
Email:
Comment:
Verification: