Casey Crookston
Casey Crookston

Reputation: 13945

Pass type as Type instead of string paramater

I would like to convert this (which is error prone)....

    public static void GenerateReport(string report)
    {
        switch (report)
        {
            case "ReportA":
                // do stuff
                break;
            case "ReportB":
                // do stuff
                break;
            case "ReportC":
                // do stuff
                break;
        }
    }

To this....

    public static void GenerateReport<T>()
    {
        switch (T) // BUT.... how do I handle this?
        {
            case ReportA:
                // do stuff
                break;
            case ReportB:
                // do stuff
                break;
            case ReportC:
                // do stuff
                break;
        }
    }

I have seen a LOT of questions that ask almost the same thing, but none of them have led me to an answer. Like this one, but for me, the solution provided in that thread flat out doesn't work. It throws multiple syntax errors when I try to compile. the solution there says:

switch typeof(T) { 
    // 
}

Upvotes: 0

Views: 74

Answers (4)

Igor
Igor

Reputation: 62213

Whenever you have if/switch statements where the code will execute differently depending on the input but produce generic output like what you have in your question it is usually a sign that you need to look into doing some refactoring.

In this case the best option would be to use an interface based design and move the logic for executing the various reports into their own types. This will allow you to better manage additional reports on an as needed basis without having to touch the existing code.

public interface IReporter {
    void GenerateReport();
}

public class ReporterA : IReporter {
    public void GenerateReport() { /* execute report */} 
}
public class ReporterB : IReporter {
    public void GenerateReport() { /* execute report */} 
}
public class ReporterC : IReporter {
    public void GenerateReport() { /* execute report */} 
}

// The responsibilty of the factory is only to create the correct reporter based on the request
public class ReporterFactory{
    public IReporter CreateReporter(string input){
        /*  the logic here can vary, you can get creative with Attributes 
            and name each report type and use reflection to create the 
            correct report type. You can also use an Enum and use that as an attribute value over
            each Reporter type. There are many ways to handle it.
        */
    }
}

/* your refactored method */
public static void GenerateReport(string report)
{
    /* again, creation pattern could be based on something other than a string. It depends on how you want to differentiate your reporters*/
    var reporter = new ReporterFactory().CreateReporter(report);
    reporter.GenerateReport();
}   

Upvotes: 1

eocron
eocron

Reputation: 7526

You can do it like this:

    public static void RunIfEqual<TLeft, TRight>(Action action)
    {
        if (typeof(TLeft) == typeof(TRight))
        {
            action();
        }
    }

    ...

    RunIfEqual<T, ReportA>(()=> ...);
    RunIfEqual<T, ReportB>(()=> ...);
    RunIfEqual<T, ReportC>(()=> ...);

Or even better way, you can define some ReportGeneratorFactory, that will choose which generator to use for this type and return it to you. Then you can just call GenerateReport on it.

Upvotes: 0

Rudu
Rudu

Reputation: 15892

You don't need generics for that, nor a switch statement with type detecting... instead use method overloading for each of the types and keep the do stuff pieces in their own methods...

public static void GenerateReport(ReportA a) { /*do stuff*/ }
public static void GenerateReport(ReportB b) { /*do stuff*/ }
public static void GenerateReport(ReportC c) { /*do stuff*/ }

Upvotes: 3

Marc Gravell
Marc Gravell

Reputation: 1062520

The key point about generics is that if the thing you are doing isn't roughly the same for each final T, then it isn't actually generic and you shouldn't be doing it that way.

Good candidates here might include:

  • an enum
  • polymorphism (a virtual / abstract method)
  • passing in a Type instance

but... not generics. The reason the language isn't helping you is because this isn't a good fit. That said: you could do:

if(typeof(T) == typeof(Foo)) {
    // foo
} else if (typeof(T) == typeof(Bar)) {
    // bar
} ...

but; that is kinda missing the point of generics.

Upvotes: 1

Related Questions