views:

324

answers:

5

I have a question. In the framework, that was largely written before the generics came, you often see a function with lots of overloads to do something with different types.

a)

Parse(int data)
Parse(long data)
Parse(string data)
..etc

That seems to be to be ok as it helps keeping code small for each method and so. On the other hand, now with generics you can do the following:

b)

Parse<T>(T data)

and then have some kind of ifs/switch statements with typeof() to try to infer what the types are and what to do with them.

What is best practise? Or what are the ideias that'd help me choose between a) and b)?

+12  A: 

IMHO, if you need if/switch statements, you should better overload. Generics should be used where the implementation does not depend on the concrete type to still reuse it.

So as a general rule:

  • overload if there will be a separate implementation for each type
  • use generics if you can have a single implementation that works for all possible types.
Stefan Steinegger
It could get a little fuzzy, if you have several types with a specific behavior and a default behavior for the rest of the types.
John Fisher
Of course, this is a simple rule for the bunch of simple cases, but its not always as simple as that. If it gets complex, one could consider to implement some pattern, like strategy, visitor or whatever.
Stefan Steinegger
@John - You could have both `Parse(int data)` (as well as other specific types) and `Parse<T>(T data)` for anything else. If one of the specific overload types is passed in, the non-generic method will be used, otherwise the type argument will be inferred by the generic method.
statenjason
I agree with this, because in general, what happens if you pass it a type it doesn't support? Easier to have a compiler error than having to throw an exception in the runtime.
Andrew Koester
+4  A: 

The pattern your describing where the use of generics results in a bunch of ifs/switch statements is an anti-pattern.

One solution to this is to implement a strategy pattern, which allows you to use generics, but at the same time isolating concerns of the Parse method from knowing how to deal with every different case.

Example:

class SomeParser
{
    void Parse<T>(ParseStrategy<T> parseStrategy, T data)
    {
        //do some prep

        parseStrategy.Parse(data);

        //do something with the result;
    }
}

class ParseStrategy<T>
{
    abstract void Parse(T data);
}

class StringParser: ParseStrategy<String>
{
    override void Parse(String data)
    {
        //do your String parsing.
    }
}

class IntParser: ParseStrategy<int>
{
    override void Parse(int data)
    {
        //do your int parsing.
    }
}

//use it like so
[Test]
public void ParseTest()
{
    var someParser = new SomeParser();
    someParser.Parse(new StringParser(), "WHAT WILL THIS PARSE TO");
}

and then you would be able to pass in any of the strategies you develop. This would allow you to properly isolate your concerns across multiple classes and not violate SRP (single responsibility principle.

Joseph
+1  A: 

While there's no one-size-fits-all rule on this, generics should be used when the specific type is irrelevant. That isn't to say that you can't constrain the type, but that specific type doesn't actually matter. In this case, the parsing method is entirely dependent on (and different for) each type. Generics don't seem like a good solution here.

Adam Robinson
+5  A: 

Code Smell.

If you have "some kind of if/switch", that is a code smell that just screams polymorphism. It suggests that generics is not the solution to that problem. Generics should be used when the code does not depend on the concrete types you pass into it.

Watch this Google Tech Talks Video: "The Clean Code Talks -- Inheritance, Polymorphism, & Testing". It addresses specifically what you are talking about.

Robert Cartaino
Yuck... Few things are harder to read than code where the programmer attempted to replace perfectly ordinary and simple if-statements with entire bloated class hierarchies.
jalf
+1  A: 

One issue here - if you're requiring if/switch statements to make generics work, you probably have a larger problem. In that situation, it is most likely that the generic argument won't work (correctly) for EVERY type, just a fixed set of types you are handling. In that case, you are much better off providing overloads to handle the specific types individually.

This has many advantages:

  • There is no chance of misuse - you're user cannot pass an invalid argument
  • There is more clarity in your API - it is very obvious which types are appropriate
  • Generic methods are more complicated to use, and not as obvious for beginning users
  • The use of a generic suggests that any type is valid - they really should work on every type
  • The generic method will likely be slower, performance wise

If your argument CAN work with any type, this becomes less clear. In that case, I'd often still consider including the overloads, plus a generic fallback method for types. This provides a performance boost when you're passing an "expected" type to the method, but you can still work with other, non-expected types.

Reed Copsey