views:

814

answers:

3

I want to write a generic class that should be casted to itself with a different generic argument.

class Base {}
class Inherited : Base {}

class MyGeneric<T> {}

// WCF Service interface
void Foo(MyGeneric<Base> b);

// somewhere else
MyGeneric<Inherited> inherited;
Foo(inherited)

I know that this could be done in C# 4.0, but this doesn't help for now.

  • I could write a specialized class for each MyGeneric<T> constellation, and there write an implicit type converter or implement a certain interface. But I want to avoid this.
  • I could have an interface with no generic, but the whole sense of the generic in this case is to get compile time type safety on the method Foo. So this is not an option.
  • Because Foo is a Operation Contract, it could not be generic itself.

Any ideas how this problem could be solved in C# 3.0?

+1  A: 

You want:

void Foo<T>(MyGeneric<T> b) where T : Base {}
Marc Gravell
Oh, yes, this works "internally", I forgot to mention that Foo is also a ServiceContract...
Stefan Steinegger
Do you mean an OperationContract?
Marc Gravell
Yes, I mean an OperationContract
Stefan Steinegger
Then I'm not sure it is "doable" under WCF, except for list types (which have different treatment).
Marc Gravell
Tried writing a Cast method. It's kind of safe, but also a little awkward. See my own answer.
Stefan Steinegger
A: 

I wrote this casting method

public MyGeneric<TTarget> Cast<TTarget, TSource>()
  where TTarget : class
  where TSource : TTarget, T
{
  return new MyGeneric<TTarget>();
}

which could be called like this

MyGeneric<Inherited> inherited;
Foo(inherited.Cast<Base, Inherited>());

The ugly thing is that one has to provide the class that it already is. There is probably some improvement possible.

By the way, I couldn't manage to make it an extension method, to avoid the second generic argument.

Stefan Steinegger
A: 

the whole sense of the generic in this case is to get compile time type safety on the method Foo

The purpose of casting in this way (to a type that is not in the instance's ancestry), is to break type safety. Don't break type safety to save type safety.

David B
Yes. That's what I'm talking about. I can't tell C# to only allow types with a generic argument that derives from that in the interface. I don't want to break type safety anywhere.
Stefan Steinegger
Working as though MyGeneric<Base> is the parent of MyGeneric<Inherited> does break type safety.
David B
If I had a Base argument and would pass an Inherited instance, it would be fine. It's normal, type safe polymorphism. MyGeneric<T> is actually a wrapper to a business object, you could see it as a business object descriptor. It replaces arguments of type Base. With the generic class I get incompatible types. And the goal is to get back the polymorphism.
Stefan Steinegger