tags:

views:

63

answers:

4

Two simple questions about generics.

Are the following two function definitions the same?

FunctionA(Exception ex);

FunctionB<T>(T ex) where T : Exception;

Are there advantages the Generic implementation (FunctionB) has over the normal implementation (FunctionA)?

+2  A: 

The following are effectively the same (in outcome):

catch (Exception e)
{
    FunctionA(e);
    FunctionB(e);
}

But it's not the same if you do this:

catch (ApplicationException e)
{
    FunctionB(e);
}
catch (Exception e)
{
    FunctionA(e);
}

This is because FunctionB gets typed to ApplicationException at compile time, wheras the call to FunctionA will always downcast the parameter to Exception. Depending on your implementation of FunctionB, this may not matter, but there are cases where it can make a difference. I would say that as a rule of thumb, if you your method implementation does not need the generic implementation, then don't use it.

Here are some examples of when it would matter:

public T AddSomeContextToThisException<T>(T ex) where T : Exception
{
    ex.Data.Add("Some key", "Some context message");
    return ex;
}

public T ThisIsABadIdeaForExceptionsButMaybeAGoodIdeaForOtherTypes<T>(T ex) where T : Exception, new()
{
    // do something with ex

    return new T();
}

The following example needs some additional code for context, so see the code beneath it:

private readonly Bar bar = new Bar();

public void HandExceptionOffToSomethingThatNeedsToBeStronglyTyped<T>(T ex) where T : Exception
{
    ICollection<T> exceptions = bar.GetCollectionOfExceptions<T>();
    exceptions.Add(ex);

    // do other stuff
}

public class Bar
{
    // value is object, because .net doesn't have covariance yet
    private Dictionary<Type, object> listsOfExceptions = new Dictionary<Type, object>();

    public ICollection<T> GetCollectionOfExceptions<T>()
    {
        if (!listsOfExceptions.ContainsKey(typeof(T)))
        {
            listsOfExceptions.Add(typeof(T), new List<T>());
        }
        return listsOfExceptions[typeof(T)] as List<T>;
    }
}
Michael Meadows
If ApplicationException was a subclass of Exception, wouldn't they still be the same though? Try/Catch blocks I understand; however, am more concerned about if there's any differences or benefits between the two.
JamesEggers
If you can't see a benefit in the implementation, then there's no benefit and very little difference. See @Samuel's answer and my comment on it for some cases when it does matter.
Michael Meadows
I understand such now. Thanks.
JamesEggers
+2  A: 

Inside the functions, there would be no difference. The difference comes from the outside. If you use the generic function, you could return the same type of exception back to the calling code instead of the base Exception. Most cases this is a moot point though.

Samuel
Matters for return type, and also for creating generic objects within the implementation itself. Also, if the generic constraints contains a "new()", then also for creating new instances of T. You're right about it being moot in most cases, however.
Michael Meadows
Given his two definition, there is no difference to inside the method. It cannot make any other assumptions besides being of type Exception. But that doesn't stop it from returning the generic type back.
Samuel
+1  A: 

There isn't much of an advantage that FunctionB has over FunctionA. However, there is a distinct difference between the two.

With FunctionA, you can pass any type that derives from (or is) an Exception into the function.

With FunctionB, you can pass any type that derives from T (which must derive from or be an Exception) into the function.

This means that depending on the type of T, you will have a more specialized parameter which will restrict the types that you can pass to it, as the parameter is going to be typed as T, not as Exception.

Now if T is an Exception, then it doesn't matter, the functions are the same. However, if T is an InvalidOperationException, then only instances of InvalidOperationException (and classes that derive from it) are permitted.

This is usually what you want when you want to ensure specialization of a type without having to resort to base classes which don't provide the level of specialization you need.

casperOne