2011-07-12 54 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; 
}