views:

106

answers:

7

Possible Duplicate:
Switch Case on type of object (C#)

I have a method signature that looks like this:

    public static TLocalType ToLocalType<TLocalType, TContract>(TContract contract)
    {

I would like to be able to do different things based on the actual type of TContract (ie call different methods). Is there a way to do something like this?

        switch (TContract)
        {
             case SomeTypeHere:
+2  A: 

Unfortunately, the best you can do is

switch(typeof(TContract).ToString()) 

Note that this includes namespaces. Note also that it will ignore inheritance and interfaces.

Peter Hallam explains why the language doesn't support this.

SLaks
A: 

The switch statement only accepts numeric and string values, which means that you can't use a Type value. You could switch off of the ToString() value of the type, but that's as close as you'll get. I would recommend using an enum instead.

Adam Robinson
+1  A: 

You can't switch on the Type object, but you can switch on it's name:

switch( typeof(TContract).Name )
{
   case "Namespace.SomeTypeName": ...
}

In general, however, this rarely proves to be a good design choice. You are better off passing in a delegate or a object that implements an interface (or even a generic type parameter) to define the "strategies" for handling different types.

This is a common question about designing and writing generic types - namely the ability to perform entirely different actions based on the type of the generic type parameters. I tend to view this as a code smell that you may not be using generics as they were intended and your design is "going against the grain", so to speak.

However, in some cases it is indeed necessary to perform processing that varies by type, in which case passing in a separate type (either to the constructor as another generic parameter) allows you to separate the different concerns into "strategies" that are more extensible, cleaner, and less coupled to the implementation of the generic class. An example might be:

interface IOperation<T>
{
    T Add( T first, T second );
}

public class Calculator<T,TOp>
   where TOp : IOperation<T>, new()
{
    private readonly TOp m_Op = new TOp();

    public T Sum( IEnumerable<T> values )
    {
        T accum = default(T);
        foreach( var val in values )
            accum = m_Op( accum, val );
        return accum;
    }
}

Now you can write different implementations of IOperation<T> that are appropriate to different types (integers, floating point numbers, complex numbers, matrixes, etc). The generic class Calculator<T,TOp> is then separate from the operations that vary by type, but is free to compose them as it sees fit.

In .NET 4.0 it's possible to use dynamic to dispatch to overloads of one or more methods that are specialized for certain types. This approach, while simpler, incurs the performance penalty of essentially invoking the compiler at runtime to figure out which method to invoke. Althought, the DLR does to a reasonably good job of caching this information at the call site, which can amortize the cost if you will invoke the method multiple times.

LBushkin
A: 

Refer to this article as to why this isnt possible.

http://blogs.msdn.com/b/peterhal/archive/2005/07/05/435760.aspx

Dested
+2  A: 

That's a fairly frequently requested feature which we are not going to do. See Peter Hallam's article on the subject for more details.

http://blogs.msdn.com/b/peterhal/archive/2005/07/05/435760.aspx

Note that if you are switching on the type of a generic then you probably have a design flaw somewhere. The whole point of generics is that they are generic; you shouldn't care what the actual type is.

Eric Lippert
There are cases where you will want to switch on a generic type parameter. For example, I once wrote a `Read<T>` extension method that switches on its parameter to read any primitive type.
SLaks
Peter Hallam proposes two unsatisfactory methods for disambiguating which switch branch to execute, but my initial reaction to the disambiguation problem would be to use the same behavior as an overloaded method with several parameter options. Why could you not use that same behavior in a switch statement?
Henry Jackson
@SLaks: Generics can handle this case more gracefully if you apply the "strategy" design pattern. Rather than switching on the type, supply an additional generic parameter that provides operations that are specific to the type the generic is being constructed for (I threw together a simple example in my answer). I tend to agree with Eric that switching on the type often implies a design flaw.
LBushkin
@LBushkin: That makes it _much_ more cumbersome to call. If you're going to do that, you might as well make separate methods with different names.
SLaks
@SLaks: First, I'm not sure I agree that this design makes the method more cumbersome to call. Perhaps more cumbersome to write correctly - but the complexity can (mostly) be hidden from consumers. Second, when you create a generic class, it's users reasonably assume that it will work correctly for any types that you substitute for it's generic arguments (within limitations imposed by generic constraints). Using a mechanism that internally switches on the type of the arguments runs counter to the core principle of generics - that they conform to arbitrary substitution of their type arguments.
LBushkin
@SLaks: I will, however, add (tongue in cheek) that it would be *wonderful* if .NET did in the future support generic constraints for constructs like operators (+,-,*,etc) so that you could more easily write generic types that could use primitive types like `int`, `long`, `decimal` in a generic manner - since the primary operations you do with such types involves the use of operators. In my experience this is one situation where type switching arises as a common (but unfortunate) solution. While there are a number other workarounds to this problem, they are all more complicated.
LBushkin
@Henry Jackson: An overloaded method with several parameter options *can give a compile-time error* if the situation cannot be resolved. What would you have us do in the analogous situation? Throw an exception at runtime?
Eric Lippert
+1  A: 

How about using Polymorphism here? I can't speak to your exact situation, but typically, when you want to switch on the type of an object, that is a sign that you may want to consider a polymorphic solution.

Ed Swangren
A: 

Switch only works for strings and other primitives.

You'd have to do something like this, where you switch off the string name of the type:

switch (typeof(T).FullName)
{
  case "System.Int32":
    Console.WriteLine("it's an int");
    break;
  case "System.String":
    Console.WriteLine("it's a string");
    break;
  default:
    Console.WriteLine("not sure");
    break;
}
David Hoerster