2011-07-12 56 views
8

我有一个包含许多代表不同物理值的字段的类。验证值的优雅方法

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

是否有任何优雅灵活的方法来完成此类验证?

+0

我想你已经做的方式是去的正确方法。唯一的变化可能是创建一个“验证器函数”,当你准备好一次检查所有*值时,你就会调用它。根据我的经验,Visual Studio(不知道你在用什么)似乎吞噬了属性设置器中发生的异常。 – jp2code

+0

@ jp2code,setter中的异常工作正常。刚刚检查。 –

回答

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

注意,这一切都不是非常i18n-友好...

+0

感谢您改进代码。但我还没有明白你的意思是“i18n友好”...... –

+0

@archer:这些信息被硬编码为英文。如果你想把它们翻译成不同的语言,你会有更多的工作要做。 –

+0

好的。谢谢你的解释。 –

1

尝试使用这样的方法:

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

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

+0

这看起来好像会引入一些开销肯定? – Skizz

+0

@Skizz,不再使用属性进行验证。我不喜欢'set'访问器中的实现验证。 –

+0

我得到的是属性和属性之间的运行时连接。也许.net在这里做一些巧妙的事情。 – Skizz

1

不是抄袭它使用我在评论中提及上面的验证功能的体统,我会做这样的事情(未测试的代码) :

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

约翰Skeet可能有更好的办法,但这个工程。 :)

3

这里是我的版本,它在某些方面比乔恩的版本更清洁了一下:

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