静态投掷类:好的或坏的练习

时间:2011-11-07 21:05:43

标签: c# design-patterns exception

抛出异常通常遵循以下模式:

if(condition) { throw exception; }

检查条件,如果条件满足,则抛出异常。所以,我想知道为它编写一个静态类是否是个好主意,可能是这样的:

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);
    }
}

(注意:此处未定义ArgumentNullOrEmptyException,但它的确与人们所期望的完全相同。)

所以不要反复写那样的东西

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

我只是做

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

我实际上喜欢它,但这也是一种很好的做法吗?

3 个答案:

答案 0 :(得分:12)

你以这种方式摆脱了一些代码重复(if ... throw),所以从这个意义上来说这是一个好主意。请注意,处理代码的人员需要知道Throw API才能阅读和理解代码。

一个改进可能是使用表达式树来摆脱传递的字符串参数名称。这将进一步提高简单性,您不必担心在重构期间键入字符串并保持其正确性。

例如,在我目前的宠物项目中,我有这个Guard类(缩短了一点):

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;
    }
}

我可以这样使用:

Guard.NotNull(() => someParameter);

答案 1 :(得分:7)

这种模式没有任何问题,我已经在很多应用程序中看到了它。这主要是个人风格的问题。

然而,有一点需要注意这个模式:它改变了资源字符串的perf语义。它的应用程序/库具有模式的本地化错误消息

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

虽然加载资源并不便宜但它也不是免费的。在上面的模式中,发生错误时按需加载资源。在你的模式中,它会被急切地加载。这意味着即使从未违反方法合同,也会急切地加载应用程序中的每个资源字符串。很可能不是你期望做的。

答案 2 :(得分:6)

我会考虑使用Code Contracts