2011-07-12 26 views
8

Tengo una clase con muchos campos que representa valores físicos diferentes.Manera elegante de validar los valores

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

Cada campo se expone mediante la propiedad de lectura/escritura. Necesito verificar en setter que el valor es correcto y generar excepción de lo contrario. Todas las validaciones son similares:

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

¿Hay alguna manera elegante y flexible para llevar a cabo dicha validación?

+0

creo que la forma en que lo ha hecho es la forma correcta de ir. El único cambio sería crear una 'función de validador 'a la que llame cada vez que esté listo para verificar * todos * sus valores al mismo tiempo. En mi experiencia, Visual Studio (no sé lo que estás usando) parece tragarse las excepciones que ocurren en un setter de propiedades. – jp2code

+0

@ jp2code, las excepciones en setter funcionan bien. Acabo de revisarlo. –

Respuesta

6

Suponiendo desea que este tipo de comportamiento, es posible considerar algunos métodos de ayuda, por ejemplo,

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

entonces usted puede escribir:

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

Si necesita este de varios tipos, incluso se podría hacer más genérica:

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

Tenga en cuenta que nada de esto es terriblemente i18n- amistoso ...

+0

Gracias por mejorar el código. Pero no he entendido lo que quiere decir con "i18n-friendly" ... –

+0

@archer: Los mensajes están codificados como en inglés. Tendrá un poco más de trabajo por delante si quiere traducirlos a un idioma diferente. –

+0

OK. Gracias por la explicación. –

3

Todo depende de lo que la tecnología que está utilizando - si eres menor de MVC puede utilizar atributos, como este;

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

+1

OP no está hablando de la validación UI ... –

+0

Tampoco yo - los atributos MVC manejan la validación del lado del cliente y del servidor (ver el método Model.IsValid) –

1

Intente utilizar tal método:

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

     proceedFunction(); 
    } 
1

se puede lograr esto utilizando las clases de System.ComponentModel.DataAnnotations

class Tunnel 
{ 
    [Range(0, double.MaxValue, ErrorMessage = "Length must be positive value.")] 
    public double Length { get; set; } 
} 

Validación:

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

también se puede aplicar atributos de su propia validación anulando ValidationAttribute clase

+0

Esto parece que va a introducir un poco de sobrecarga seguramente? – Skizz

+0

@Skizz, es validación utilizando atributos, no más. No me gusta la validación de implementación dentro del acceso de 'set'. –

+0

a lo que me refería era a la conexión de tiempo de ejecución entre la propiedad y el atributo. Quizás .net hace algo inteligente aquí. – Skizz

1

Uso de la función de validación he mencionado en mi comentario anterior, me gustaría hacer algo como esto (código no probado) :

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 probablemente tiene una mejor manera, pero esto funciona. :)

3

aquí está mi versión, que es un poco más limpio que la versión de Jon en algunos aspectos:

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