简体   繁体   中英

Static Throw class: good or bad practice

Throwing exceptions often follows the following pattern:

if(condition) { throw exception; }

you check a condition, and if the condition is satisfied, you throw an exception. So, i was wondering if it is good idea to write a static class for it that could look like this:

public static class Throw
{
    public static void IfNullOrEmpty<T>(string @string, params object[] parameters) where T : Exception
    {
        Throw.If<T>(string.IsNullOrEmpty(@string), parameters);
    }

    public static void IfNullOrEmpty<T, I>(IEnumerable<I> enumerable, params object[] parameters) where T : Exception
    {
        Throw.If<T>(enumerable == null || enumerable.Count() == 0, parameters);
    }

    public static void IfNullOrEmpty(string @string, string argumentName)
    {
        Throw.IfNullOrEmpty(@string, argumentName, 
            string.Format("Argument '{0}' cannot be null or empty.", argumentName));
    }

    public static void IfNullOrEmpty(string @string, string argumentName, string message)
    {
        Throw.IfNullOrEmpty<ArgumentNullOrEmptyException>(@string, message, argumentName);
    }

    public static void IfNullOrEmpty<I>(IEnumerable<I> enumerable, string argumentName)
    {
        Throw.IfNullOrEmpty(enumerable, argumentName, 
            string.Format("Argument '{0}' cannot be null or empty.", argumentName));
    }

    public static void IfNullOrEmpty<I>(IEnumerable<I> enumerable, string argumentName, string message)
    {
        Throw.IfNullOrEmpty<ArgumentNullOrEmptyException, I>(enumerable, message, argumentName);
    }


    public static void IfNull<T>(object @object, params object[] parameters) where T : Exception
    {
        Throw.If<T>(@object == null, parameters);
    }

    public static void If<T>(bool condition, params object[] parameters) where T : Exception
    {
        if (condition) 
        {
            var types = new List<Type>();
            var args = new List<object>();
            foreach (object p in parameters ?? Enumerable.Empty<object>())
            {
                types.Add(p.GetType());
                args.Add(p);
            }

            var constructor = typeof(T).GetConstructor(types.ToArray());
            var exception = constructor.Invoke(args.ToArray()) as T;
            throw exception;
        }
    }

    public static void IfNull(object @object, string argumentName)
    {
        Throw.IfNull<ArgumentNullException>(@object, argumentName);
    }
}

(Note: The ArgumentNullOrEmptyException is not defined here, but it does pretty much what one would expect.)

so instead of repeatedly writing stuff like that

void SomeFunction(string someParameter)
{
   if(string.IsNullOrEmpty(someParameter))
   {
      throw new ArgumentNullOrEmptyException("someParameter", "Argument 'someParameter' cannot be null or empty.");
   }
}

i just do

void SomeFunction(string someParameter)
{
   Throw.IfNullOrEmpty(someParameter, "someParameter"); // not .IsNullOrEmpty
}

i actually do like it, but is it also a good practice?

You get rid of a bit of code duplication this way (the if ... throw), so in that sense it is a good idea. Just be aware that people working on the code would need to know the Throw API to be able to read and understand the code.

One improvement could be to use expression trees to get rid of the string parameter name passing. This would improve the simplicity further, and you wouldn't have to worry about typing the strings and keeping them correct during refactorings and such.

For instance, on my current pet project I have this Guard class (shortened a bit):

public static class Guard
{
    public static void NotNullOrEmpty(Expression<Func<string>> parameterExpression)
    {
        string value = parameterExpression.Compile()();
        if (String.IsNullOrWhiteSpace(value))
        {
            string name = GetParameterName(parameterExpression);
            throw new ArgumentException("Cannot be null or empty", name);
        }
    }

    public static void NotNull<T>(Expression<Func<T>> parameterExpression)
        where T : class
    {
        if (null == parameterExpression.Compile()())
        {
            string name = GetParameterName(parameterExpression);
            throw new ArgumentNullException(name);
        }
    }

    private static string GetParameterName<T>(Expression<Func<T>> parameterExpression)
    {
        dynamic body = parameterExpression.Body;
        return body.Member.Name;
    }
}

Which I can then use like this:

Guard.NotNull(() => someParameter);

There is nothing wrong with this pattern and I've seen it done in a number of applications. It's mostly a matter of personal style.

However there is one thing to be aware of with this pattern though: it changes the perf semantics for resource strings. It apps / libraries which have localized error messages the pattern is

if (...) {
  throw new ArgumentExecption("paramName", LoadSomeResource(ErrorId));
}

While loading a resource is not cheap it's not free either. In the pattern above the resource is loaded on demand when an error occurs. In your pattern it would be loaded eagerly instead. This means that every resource string in the application would be loaded eagerly even if there were never any violation of method contracts. Very likely not what you were expecting to do.

我会考虑使用代码合约

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM