Prevent a particular hierarchy of types being used for a generic parameter

I started this question with a load of background of the types in question; the interfaces and rationale behind the architecture.

Then I realised - 'This is SO - keep it simple and get to the point'.

So here goes.

I have a class like this:

public class AGenericType<T> : AGenericTypeBase
{
  T Value { get; set; }
}

.Net then, of course, allows me to do this:

AGenericType<AGenericType<int>> v;

However, in the context of AGenericType's usage, it is a nonsense to do this in the same way that it's a nonsense to do this:

Nullable<Nullable<double>> v;

What I want to be able to do is restrict this generic type so that it becomes impossible to create such an instance or even declare a reference to the type when it's T is derived from AGenericTypeBase - preferably at compile-time.

Now an interesting thing here is that the Nullable example I give here does indeed generate a compiler error. But I can't see how Nullable restricts the T to non-Nullable types - since Nullable is a struct, and the only generic constraint I can find (even in the IL, which often yields compiler secrets, like those with delegates) is where T:struct. So I'm thinking that one must be a compiler hack (EDIT: See @Daniel Hilgarth's answer + comments below for a bit of an exploration of this). A compiler hack I can't repeat of course!

For my own scenario, IL and C# don't allow a negative-assert constraint like this:

public class AGenericType<T> : where !T:AGenericTypeBase

(note the '!' in the constraint)

But what alternative can I use?

I've thought of two:

1) Runtime exception generated in the constructor of an AGenericType:

public AGenericType(){
  if(typeof(AGenericBase).IsAssignableFrom(typeof(T)))
    throw new InvalidOperationException();
}

That doesn't really reflect the nature of the error, though - because the problem is the generic parameter and therefore the whole type; not just that instance.

2) So, instead, the same runtime exception, but generated in a static initializer for AGenericType:

static AGenericType(){
  if(typeof(AGenericBase).IsAssignableFrom(typeof(T)))
    throw new InvalidOperationException();
}

But then I'm faced with the problem that this exception will be wrapped inside a TypeInitializationException and could potentially cause confusion (in my experience developers that actually read a whole exception hierarchy are thin on the ground).

To me, this is a clear case for 'negative assertion' generic constraints in IL and C# - but since that's unlikely to happen, what would you do?

5
задан Andras Zoltan 19 May 2011 в 05:36
поделиться