验证构造函数参数的正确方法是什么

时间:2022-10-22 10:43:07

So I have a class with a single string parameter in its constructor:

所以我的构造函数中有一个带有单个字符串参数的类:

public MyClass(string name)
{
    this.Name = name;
}

but I never want to worry about some one initializing an instance of MyClass with name that is NULL or a zero length string. What is the best was to validate the parameter? With a property or method you can always just do nothing, return null, or not set anything if an invalid argument is provided. But once a constructor is called the instance of the object is already created regardless of whether it was passed valid arguments.

但我永远不想担心有人初始化一个名为NULL或零长度字符串的MyClass实例。什么是最好的验证参数?使用属性或方法,如果提供了无效参数,您可以随时执行任何操作,返回null或不设置任何内容。但是一旦调用了构造函数,就会创建该对象的实例,无论它是否传递了有效的参数。

Is throwing an exception as shown below the best way? Or is there some other method that's more preferred?

如下所示抛出异常最好的方法?或者是否有其他更优选的方法?

public MyClass(string name)
{
    if (name == null | name == "") throw new ArgumentException("Name can not be null or blank", "name");

    this.Name = name;
}

Of course I could always set the name to some default like "Unnamed MyClass" if the argument is invalid, but in this case I'd rather just prevent an invalid name then trying to assume some alternate behavior.

当然,如果参数无效,我总是可以将名称设置为某些默认名称,如“Unnamed MyClass”,但在这种情况下,我宁愿只是阻止一个无效的名称,然后尝试假设一些替代行为。

5 个解决方案

#1


  1. Throw an ArgumentNullException
  2. 抛出ArgumentNullException

  3. Document that the ctor throws ArgumentNullException if name is null
  4. 如果name为null,则记录ctor抛出ArgumentNullException

  5. If you are using Code Contracts, add a Contract.EndContractBlock() line after your parameter validation.
  6. 如果您使用的是代码约定,请在参数验证后添加Contract.EndContractBlock()行。

Edit: Instead of this:

编辑:而不是这个:

if (name == null || name == "")

Use this:

if (string.IsNullOrEmpty(name))

#2


The preferred solution here is to throw an exception. Fail early, fail often. Document what the valid parameters are for your constructor and that it throws an ArgumentNullException or ArgumentOutOfRangeException on invalid parameters.

这里的首选解决方案是抛出异常。早退,经常失败。记录构造函数的有效参数,并在无效参数上抛出ArgumentNullException或ArgumentOutOfRangeException。

In my opinion, the salient point is that you don't want to silently absorb failures. Suppose the user typed in a name incorrectly (accidentally left it null, for example). Better to have the operation fail and return notification to the user than to swallow the failure (with, say, a default) and have the user unaware they mistyped their entry.

在我看来,重点是你不想默默地吸收失败。假设用户输入的名称不正确(例如,意外地将其保留为空)。最好让操作失败并向用户返回通知而不是吞下失败(例如,默认)并让用户不知道他们错误输入了他们的条目。

I asked a similar question awhile back to settle an argument with some colleagues.

我回答了一些类似的问题,以便与一些同事解决争执。

"But once a constructor is called the instance of the object is already created regardless of whether it was passed valid arguments."

“但是一旦调用了构造函数,就会创建对象的实例,无论它是否传递了有效的参数。”

The object is created (i.e., non-null) only if the constructor returns normally.

仅当构造函数正常返回时才创建对象(即,非null)。

#3


You can either set the name to some default or throw an exception. Anything else is just plain wrong since it would create an object with a non-valid state.

您可以将名称设置为某个默认值或抛出异常。其他任何东西都是完全错误的,因为它会创建一个具有无效状态的对象。

#4


If passing an empty or null name represents an error, throwing an exception is the reasonable thing to do. Ignoring the null name and doing some arbitrary recovery is just masking the error and becomes likely to lead to invalid state.

如果传递空名或空名表示错误,则抛出异常是合理的。忽略空名称并执行一些任意恢复只是掩盖错误并可能导致无效状态。

If not passing a name is a valid way to construct the object, offer a parameterless constructor.

如果不传递名称是构造对象的有效方法,则提供无参数构造函数。

#5


There is a Guard utility class that you might find useful for validating arguments passed to any method.

有一个Guard实用程序类,您可能会发现它可用于验证传递给任何方法的参数。

The class is avaialable here using System; using System.Globalization;

这个类在这里可以使用System;使用System.Globalization;

    namespace Microsoft.Practices.Mobile.CompositeUI.Utility
    {
        /// <summary>
        /// Common guard clauses.
        /// </summary>
        public static class Guard
        {


    /// <summary>
        /// Checks a string argument to ensure it isn't null or empty.
        /// </summary>
        /// <param name="argumentValue">The argument value to check.</param>
        /// <param name="argumentName">The name of the argument.</param>
        public static void ArgumentNotNullOrEmptyString(string argumentValue, string argumentName)
        {
            ArgumentNotNull(argumentValue, argumentName);

            if (argumentValue.Trim().Length == 0)
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Properties.Resources.StringCannotBeEmpty, argumentName));
        }

        /// <summary>
        /// Checks an argument to ensure it isn't null.
        /// </summary>
        /// <param name="argumentValue">The argument value to check.</param>
        /// <param name="argumentName">The name of the argument.</param>
        public static void ArgumentNotNull(object argumentValue, string argumentName)
        {
            if (argumentValue == null)
                throw new ArgumentNullException(argumentName);
        }

        /// <summary>
        /// Checks an Enum argument to ensure that its value is defined by the specified Enum type.
        /// </summary>
        /// <param name="enumType">The Enum type the value should correspond to.</param>
        /// <param name="value">The value to check for.</param>
        /// <param name="argumentName">The name of the argument holding the value.</param>
        public static void EnumValueIsDefined(Type enumType, object value, string argumentName)
        {
            if (Enum.IsDefined(enumType, value) == false)
                throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
                    Properties.Resources.InvalidEnumValue,
                    argumentName, enumType.ToString()));
        }

        /// <summary>
        /// Verifies that an argument type is assignable from the provided type (meaning
        /// interfaces are implemented, or classes exist in the base class hierarchy).
        /// </summary>
        /// <param name="assignee">The argument type.</param>
        /// <param name="providedType">The type it must be assignable from.</param>
        /// <param name="argumentName">The argument name.</param>
        public static void TypeIsAssignableFromType(Type assignee, Type providedType, string argumentName)
        {
            if (!providedType.IsAssignableFrom(assignee))
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
                    Properties.Resources.TypeNotCompatible, assignee, providedType), argumentName);
        }
    }
}

#1


  1. Throw an ArgumentNullException
  2. 抛出ArgumentNullException

  3. Document that the ctor throws ArgumentNullException if name is null
  4. 如果name为null,则记录ctor抛出ArgumentNullException

  5. If you are using Code Contracts, add a Contract.EndContractBlock() line after your parameter validation.
  6. 如果您使用的是代码约定,请在参数验证后添加Contract.EndContractBlock()行。

Edit: Instead of this:

编辑:而不是这个:

if (name == null || name == "")

Use this:

if (string.IsNullOrEmpty(name))

#2


The preferred solution here is to throw an exception. Fail early, fail often. Document what the valid parameters are for your constructor and that it throws an ArgumentNullException or ArgumentOutOfRangeException on invalid parameters.

这里的首选解决方案是抛出异常。早退,经常失败。记录构造函数的有效参数,并在无效参数上抛出ArgumentNullException或ArgumentOutOfRangeException。

In my opinion, the salient point is that you don't want to silently absorb failures. Suppose the user typed in a name incorrectly (accidentally left it null, for example). Better to have the operation fail and return notification to the user than to swallow the failure (with, say, a default) and have the user unaware they mistyped their entry.

在我看来,重点是你不想默默地吸收失败。假设用户输入的名称不正确(例如,意外地将其保留为空)。最好让操作失败并向用户返回通知而不是吞下失败(例如,默认)并让用户不知道他们错误输入了他们的条目。

I asked a similar question awhile back to settle an argument with some colleagues.

我回答了一些类似的问题,以便与一些同事解决争执。

"But once a constructor is called the instance of the object is already created regardless of whether it was passed valid arguments."

“但是一旦调用了构造函数,就会创建对象的实例,无论它是否传递了有效的参数。”

The object is created (i.e., non-null) only if the constructor returns normally.

仅当构造函数正常返回时才创建对象(即,非null)。

#3


You can either set the name to some default or throw an exception. Anything else is just plain wrong since it would create an object with a non-valid state.

您可以将名称设置为某个默认值或抛出异常。其他任何东西都是完全错误的,因为它会创建一个具有无效状态的对象。

#4


If passing an empty or null name represents an error, throwing an exception is the reasonable thing to do. Ignoring the null name and doing some arbitrary recovery is just masking the error and becomes likely to lead to invalid state.

如果传递空名或空名表示错误,则抛出异常是合理的。忽略空名称并执行一些任意恢复只是掩盖错误并可能导致无效状态。

If not passing a name is a valid way to construct the object, offer a parameterless constructor.

如果不传递名称是构造对象的有效方法,则提供无参数构造函数。

#5


There is a Guard utility class that you might find useful for validating arguments passed to any method.

有一个Guard实用程序类,您可能会发现它可用于验证传递给任何方法的参数。

The class is avaialable here using System; using System.Globalization;

这个类在这里可以使用System;使用System.Globalization;

    namespace Microsoft.Practices.Mobile.CompositeUI.Utility
    {
        /// <summary>
        /// Common guard clauses.
        /// </summary>
        public static class Guard
        {


    /// <summary>
        /// Checks a string argument to ensure it isn't null or empty.
        /// </summary>
        /// <param name="argumentValue">The argument value to check.</param>
        /// <param name="argumentName">The name of the argument.</param>
        public static void ArgumentNotNullOrEmptyString(string argumentValue, string argumentName)
        {
            ArgumentNotNull(argumentValue, argumentName);

            if (argumentValue.Trim().Length == 0)
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Properties.Resources.StringCannotBeEmpty, argumentName));
        }

        /// <summary>
        /// Checks an argument to ensure it isn't null.
        /// </summary>
        /// <param name="argumentValue">The argument value to check.</param>
        /// <param name="argumentName">The name of the argument.</param>
        public static void ArgumentNotNull(object argumentValue, string argumentName)
        {
            if (argumentValue == null)
                throw new ArgumentNullException(argumentName);
        }

        /// <summary>
        /// Checks an Enum argument to ensure that its value is defined by the specified Enum type.
        /// </summary>
        /// <param name="enumType">The Enum type the value should correspond to.</param>
        /// <param name="value">The value to check for.</param>
        /// <param name="argumentName">The name of the argument holding the value.</param>
        public static void EnumValueIsDefined(Type enumType, object value, string argumentName)
        {
            if (Enum.IsDefined(enumType, value) == false)
                throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
                    Properties.Resources.InvalidEnumValue,
                    argumentName, enumType.ToString()));
        }

        /// <summary>
        /// Verifies that an argument type is assignable from the provided type (meaning
        /// interfaces are implemented, or classes exist in the base class hierarchy).
        /// </summary>
        /// <param name="assignee">The argument type.</param>
        /// <param name="providedType">The type it must be assignable from.</param>
        /// <param name="argumentName">The argument name.</param>
        public static void TypeIsAssignableFromType(Type assignee, Type providedType, string argumentName)
        {
            if (!providedType.IsAssignableFrom(assignee))
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
                    Properties.Resources.TypeNotCompatible, assignee, providedType), argumentName);
        }
    }
}