简体   繁体   English

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

[英]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.) (注意:这里没有定义ArgumentNullOrEmptyException ,但是它几乎与人们期望的一样。)

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. 请注意,处理代码的人需要知道Throw API才能读取和理解代码。

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): 例如,在我目前的宠物项目中,我有这个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;
    }
}

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. 但是,有一点需要注意这种模式:它改变了资源字符串的perf语义。 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. 很可能不是你期望做的。

我会考虑使用代码合约

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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