优雅的方式来验证价值观

时间:2011-07-12 14:56:47

标签: c# .net validation

我有一个包含许多字段的类,它们代表不同的物理值。

class Tunnel
{
    private double _length;
    private double _crossSectionArea;
    private double _airDensity;
    //...

使用读/写属性公开每个字段。我需要检查setter的值是否正确,否则生成异常。所有验证都是类似的:

    public double Length
    {
        get { return _length; }
        set
        {
            if (value <= 0) throw new ArgumentOutOfRangeException("value",
                    "Length must be positive value.");
            _length = value;
        }
    }

    public double CrossSectionArea
    {
        get { return _crossSectionArea; }
        set
        {
            if (value <= 0) throw new ArgumentOutOfRangeException("value",
                    "Cross-section area must be positive value.");
            _crossSectionArea = value;
        }
    }

    public double AirDensity
    {
        get { return _airDensity; }
        set
        {
            if (value < 0) throw new ArgumentOutOfRangeException("value",
                    "Air density can't be negative value.");
            _airDensity = value;
        }
    }
    //...

有没有优雅而灵活的方法来完成此类验证?

7 个答案:

答案 0 :(得分:6)

假设您想要这种行为,您可以考虑一些辅助方法,例如

public static double ValidatePositive(double input, string name)
{
    if (input <= 0)
    {
        throw new ArgumentOutOfRangeException(name + " must be positive");
    }
    return input;
}

public static double ValidateNonNegative(double input, string name)
{
    if (input < 0)
    {
        throw new ArgumentOutOfRangeException(name + " must not be negative");
    }
    return input;
}

然后你可以写:

public double AirDensity
{
    get { return _airDensity; }
    set
    {            
        _airDensity = ValidationHelpers.ValidateNonNegative(value,
                                                            "Air density");
    }
}

如果你需要各种类型,你甚至可以使它通用:

public static T ValidateNonNegative(T input, string name)
    where T : IComparable<T>
{
    if (input.CompareTo(default(T)) < 0)
    {
        throw new ArgumentOutOfRangeException(name + " must not be negative");
    }
    return input;
}

请注意,这些都不是非常友好的......

答案 1 :(得分:3)

一切都取决于您使用的技术 - 如果您使用的是MVC,您可以使用属性,就像这样;

http://msdn.microsoft.com/en-us/library/ee256141(v=vs.98).aspx

答案 2 :(得分:3)

这是我的版本,它在某些方面比Jon的版本更清晰:

interface IValidator <T>
{
  bool Validate (T value);
}

class IntValidator : IValidator <int>
{
  public bool Validate (int value)
  {
    return value > 10 && value < 15;
  }
}
class Int2Validator : IValidator<int>
{
  public bool Validate (int value)
  {
    return value > 100 && value < 150;
  }
}

struct Property<T, P> where P : IValidator<T>, new ()
{
  public T Value
  {
    set
    {
      if (m_validator.Validate (value))
      {
        m_value = value;
      }
      else
      {
        Console.WriteLine ("Error validating: '" + value + "' is out of range.");
      }
    }

    get { return m_value; }
  }

  T m_value;
  static IValidator<T> m_validator=new P();
}

class Program
{
  static void Main (string [] args)
  {
    Program
      p = new Program ();

    p.m_p1.Value = 9;
    p.m_p1.Value = 12;
    p.m_p1.Value = 25;
    p.m_p2.Value = 90;
    p.m_p2.Value = 120;
    p.m_p2.Value = 250;
  }

  Property<int, IntValidator>
    m_p1;

  Property<int, Int2Validator>
    m_p2;
}

答案 3 :(得分:1)

尝试使用这样的方法:

 public void FailOrProceed(Func<bool> validationFunction, Action proceedFunction, string errorMessage)
    {
        // !!! check for nulls, etc
        if (!validationFunction())
        {
            throw new ArgumentOutOfRangeException(errorMessage);
        }

        proceedFunction();
    }

答案 4 :(得分:1)

您可以使用System.ComponentModel.DataAnnotations

中的类来实现此目的
class Tunnel
{
    [Range(0, double.MaxValue, ErrorMessage = "Length must be positive value.")]
    public double Length { get; set; }
}

验证:

var tunnel = new Tunnel { Length = 0 };
var context = new ValidationContext(tunnel, null, null);
Validator.ValidateObject(tunnel, context, true);

您还可以实现自己的验证属性,覆盖ValidationAttribute

答案 5 :(得分:1)

是的,通过创建自己的验证属性。

阅读这篇文章:Business Object Validation Using Attributes in C#

我将在这里:)

正确地复制它

答案 6 :(得分:1)

使用我在上面的评论中提到的Validator函数,我会做这样的事情(未经测试的代码):

void textBox_Changed(object sender, EventArgs e) {
  submitButton.Enabled = validator();
}

bool validator() {
  const string NON_POSITIVE = "Value must be greater than Zero";
  bool result = false;
  string controlName = "Length";
  try {
    _length = Convert.ToDouble(txtLength.Text);
    if (_length <= 0) throw new Exception(NON_POSITIVE);
    controlName = "Cross Section Area";
    _crossSectionArea = Convert.ToDouble(txtCrossSectionArea.Text);
    if (_crossSectionArea <= 0) throw new Exception(NON_POSITIVE);
    controlName = "Air Density";
    _airDensity = Convert.ToDouble(txtAirDensity.Text);
    if (_airDensity <= 0) throw new Exception(NON_POSITIVE);
    result = true; // only do this step last
  } catch (Exception err) {
    MessageBox.Show(controlName + " Error: " + err.Message, "Input Error");
  }
  return result;
}

John Skeet可能有更好的方法,但这很有效。 :)