Substitutability of generic types in .NET

The “is” operator

Anyone using object orientated programming soon finds themselves wanting to perform some action only if an object is of some type or implements some interface.

The easiest way is to do this is to use the “is” operator. Typically it will appear something like;

object objA = FunctionToGetAnObject();
if (obj is SomeClassOrInterface)
    ((SomeClassOrInterface) obj).SomeMethod();

Here if obj_A is of a type that implements SomeClassOrInterface anywhere in its inheritence tree then SomeMethod of SomeClassOrInterface will be called.

Test substitution with generic types

Now lets say we have our own generic type – here’s one to illustrate a point;

public class DamoClass<T> {
    public T GetProperty { get { ... } }
    public T SetProperty { set { ... } }
    public bool IsValid { return true; }
}

If we want to check obj_A supports DamoClass<T> but we don’t care what type T is you might expect to be able to perform;

if (objA is DamoClass)

But that results in a compilation error – you can’t reference a generic class without specifying the type parameters regardless of whether you care about them or not. You might then try;

if (objA is DamoClass<object>)

After all every class inherits from object. This compiles fine but at runtime if objA is of any type other than object, e.g. DamoClass<string> it will return false.

Why?

The following might be what you had in mind;

if (objA is DamoClass<object>) {
    ((DamoClass<object>) objA).IsValid();
    object test = ((DamoClass<object>) objA).GetProperty;
}

Which looks fine but if .NET allowed that then how would it prevent;

if (objA is DamoClass<object>)
    ((DamoClass<object>) objA).SetProperty = 11;

When objA.AssociatedObject might well be something other than an integer? It can’t – so it doesn’t.

Solution

The solution is to split your generic class into two classes.

The first contains your non-generic methods and properties and the second inherits from the first adding all the generic specialisation. The good news is that they can even have the same name.

public class DamoClass {
    public object GetProperty { get { ... } }
    public bool IsValid { return true; }
}

public class DamoClass<T> : DamoClass {
    public T SetProperty { set { ... } }
}

Then you are free to do;

if (objA is DamoClass) {
    ((DamoClass) objA).IsValid();
    object test = ((DamoClass) objA).GetProperty;
}

Indeed .NET itself uses this technique for IEnumerable and IEnumerable

One drawback with this technique is that you can’t redefine the type of a member in the subclass – maybe that’ll make C# 3.0.

[)amien

1 responses

  1. Avatar for Steve

    Generally I treat this problem with some caution - often needing to perform conditional behaviour based on typeof() or equivalent is a sign that logic may be in the wrong place. Often this behaviour can be pushed down to the objects themselves through some more generic concept or event behaviour and let polymorphism deal with it. Another way, appropriate if the algorithm is designed to be external to the classes themselves (perhaps because it is pluggable) is with a visitor pattern, where there are multiple overloaded visit(T obj) methods - code flow naturally follows the implementation for the given type.

    Whatever the way, although I do very occasionally use typeof() style logic, I generally feel uncomfortable about it since it normally means I'm not thinking about something the right way. I won't say I've never done it, though ;)

    Steve 3 May 2006