Search code examples
c#exceptiongenericsfactory-pattern

Assign Generic Exception <TException> a message


Since I do a lot of argument null checking, I wanted to simplify it a little bit. I created the following method:

public static void ThrowExceptionIf<TException>(bool condition, string message = null, params KeyValuePair<string, string>[] data) where TException : Exception, new()
{
    if (condition)
    {
        return;
    }

    var exception = new TException();
    data.AsParallel().ForAll(d => exception.Data.Add(d.Key, d.Value));

    throw exception;
}

Which is used like:

public ValidatableBase(IUser user, IEventAggregator eventService)
    : this()
{
    ExceptionFactory
        .ThrowExceptionIf<ArgumentNullException>(user == null || eventService == null);
    this.user = user;
    this.EventService = eventService;
}

The problem however is that I can't assign a Message to the exception. The Message property is readonly and the generic doesn't see that the constrained Type accepts parameters.

I've seen some people instance a CustomException and pass the message in through its constructor, and assign the InnerException as a given exception (in this case TException) but I wanted to ask if there was any alternatives before going that route. Wrapping all exceptions thrown by the factory in a custom exception seems like bad design.

Edit

The final factory uses a combination of everyones answers and works great.

public static void ThrowExceptionIf<TException>(bool condition, string message = null, IUser user = null, params KeyValuePair<string, string>[] data) where TException : Exception, new()
{
    ThrowExceptionIf<TException>(
        condition,
        () => (TException) Activator.CreateInstance(typeof (TException), message), 
        user, 
        data);
}

public static void ThrowExceptionIf<TException>(Func<bool> predicate, string message = null, IUser user = null, params KeyValuePair<string, string>[] data) where TException : Exception, new()
{
    ThrowExceptionIf<TException>(predicate(), message, user, data);
}

public static void ThrowExceptionIf<TException>(bool condition, Func<TException> exception, IUser user = null, params KeyValuePair<string, string>[] data) where TException : Exception, new()
{
    if (condition)
    {
        return;
    }

    TException exceptionToThrow = exception();
    AddExceptionData(exceptionToThrow, user, data);

    throw exceptionToThrow;
}

public static void AddExceptionData(Exception exception, IUser user = null, params KeyValuePair<string, string>[] data)
{
    foreach (var exceptionData in data)
    {
        exception.Data.Add(exceptionData.Key, exceptionData.Value);
    }

    if (user != null)
    {
        exception.Data.Add("User", user.RacfId);
    }
}

I can now use the ThrowExceptionIf method in the following ways

ThrowExceptionIf<ArgumentNullException>(
    user == null,
    "Users can not be null.");

ThrowExceptionIf<ArgumentNullException>(
    user == null,
    () => new ArgumentNullException("user", "Users can not be null"));

ThrowExceptionIf<ArgumentException>(
    () => user.FirstName.Equals(user.LastName),
    "last name must not equal the first name");

Thanks for the help!


Solution

  • You can also pass a creation lambda method, rather than a message, to the throwing method:

    public static void ThrowExceptionIf<TException>(bool condition, Func<TException> init, params KeyValuePair<string, string>[] data) where TException : Exception
    {
        if (condition)
        {
            return;
        }
    
        var exception = init();
        data.AsParallel().ForAll(d => exception.Data.Add(d.Key, d.Value));
    
        throw exception;
    }
    

    Then call like ThrowExceptionIf(true, ()=>new ArgumentNullException("foo"));.