views:

24

answers:

0

(I applogise in advance for the length of this post, however the problem is fairly complex - hopefully everything is clear however I have simplified the scenario a lot and so there is a good chance that I've missed a vital fact, or there is something that I've not explained.)

Setting the scene

I'm using code generation to generate C# wrapper / interop classes for custom types defined in a domain specific language. These custom types support named attributes of various types, and are currently exposed to .Net code as an instance of a CustomType class:

// Simple example use of CustomType class
void Test(CustomType widget)
{
    string someString = widget.GetString("SomeString");
    int myInteger = widget.GetInt("MyInteger");
    CustomType gadget = widget.GetCustomType("MyGadget");
}

These types also support a simple inheritance heirachy in that if a custom type "Widget" inherits from another type "Gadget", then an instance of "Widget" will inherit all the attributes defined on the "Gadget" type as well as its own attributes.

Of course if you try to "Get" an attribute of the wrong type (or an attribute that doesn't exist) then things go horribly wrong - my generation process produces a class for each custom type that has generated properties with the correct names / types:

void Test(Widget widget)
{
    string someString = widget.SomeString;
    int myInteger = widget.MyInteger;
    Gadget gadget = widget.MyGadget;
}

I've been using this for a while now and it helps out a lot with catching all the silly type conversion errors at compile time rather than runtime, however there is one remaining thing thats niggling at me:

How should I support "casting" of these generated types?

I've also added the ability to cast between these generated custom types, for example:

// Remember that Widget : Gadget
Widget widget = new Widget();
Gadget gadget = widget; // Implicit cast
Widget widget = (Widget)gadget; // Explicit cast
Bucket bucket = (Bucket)widget;  // Invalid cast causes compile time error (Bucket is another generated type)

Again all of the above works (using cast operator overloading), however the problem start to come when I don't know what the custom type is:

void Test(CustomType someObject)
{
    Widget widget = (widget)someObject;
}
// And (more tricky)
void Test(object someObject)
{
    Widget widget = (widget)someObject;
}

As far as I can see I have three ways to implement this:

1) If I do the obvious thing and have my generated types inherit from each other and CustomType then I get problems because I can't overload casting between types that inherit from each other:

void Test(CustomType someObject)
{
    // If someObject is actually an instance of the .Net CustomType class then the following cast fails
    // even if the underlying object is really a widget.
    Widget widget = (widget)someObject;
}

This means that casts (such as the above) may or may not fail depending on the way that the custom object is instantiated, which is incredibly scary! For various reasons it is very difficult to control the way that these objects are instantiated (for example, most of the time instantiating custom types is done in common assemblies that have no knowledge of the generated types)

2) The alternative that I've gone for is to have none of my generated types inherit from each other and instead generate suitable cast operator overloading methods in each of my generated types, this means that the above example works, however I have problems with unboxing object types:

void Test(object someObject)
{
    // Again If someObject is actually an instance of CustomType then the following all goes wrong
    Widget widget = (widget)someObject;
}

This is much much better as I can guarentee that casts between generated types will work consistently, and I only need to cast to a custom type from object at a couple of places (and in those cases it is normally safe to assume that it is a CustomType), however it feels really messy to have all those operator overloads, and casting from object is a faff:

void Test(object someObject)
{
    Widget widget = (widget)(CustomType)someObject;
}

3) Failing that, I can scrap casting and force people to create new instances of the generated types when they "cast":

void Test(object someObject)
{
    Widget widget = new Widget(someObject);
    Gadget gadget = new Gadget(widget);
}

But this means I loose the compile time type safety when converting between types (as the objects all need to accept a constructor with an input of type object), and looks messy.

My current favourite is #2 - Is there any way to get casting of these generated to work any nicer?

Failing that, has anyone got any alternative suggestions (for example a way of creating instances of the correct wrapper object in assemblies that don't "know" about the generated types?)