2011-11-27 21 views
6

Me gustaría aplicar una estructura para que siempre sea válida con respecto a un contrato determinado, impuesto por el constructor. Sin embargo, el operador default viola el contrato.Cómo puedo hacer cumplir un contrato dentro de una estructura

considere lo siguiente, por ejemplo:

struct NonNullInteger 
{ 
    private readonly int _value; 

    public int Value 
    { 
     get { return _value; } 
    } 

    public NonNullInteger(int value) 
    { 
     if (value == 0) 
     { 
      throw new ArgumentOutOfRangeException("value"); 
     } 

     _value = value; 
    } 
} 

// Somewhere else: 
var i = new NonNullInteger(0); // Will throw, contract respected 
var j = default(NonNullInteger); // Will not throw, contract broken 

Como solución he cambiado de estructura a una clase para que pueda garantizar el constructor siempre se llama al inicializar una nueva instancia. Pero me pregunto, ¿hay absolutamente no hay forma de obtener el mismo comportamiento con una estructura?

Respuesta

3

Un enfoque consiste en disponer las cosas para que el valor predeterminado satisface el contrato:

struct NonNullInteger 
{ 
    private readonly int _valueMinusOne; 

    public int Value 
    { 
     get { return _valueMinusOne + 1; } 
    } 

    public NonNullInteger(int value) 
    { 
     if (value == 0) 
     { 
      throw new ArgumentOutOfRangeException("value"); 
     } 

     _valueMinusOne = value - 1; 
    } 
} 
+0

Este en realidad es una buena solución para cumplir con la letra del contrato, aunque también necesitaría verificar int.MinValue. No recomendaría crear esta estructura, pero, si realmente quisiera hacerlo, esta es probablemente la solución más limpia. –

+0

+1 truco inteligente. Sin embargo, puede ser bastante más difícil para contratos más complejos. –

6

No veo cómo podría hacer esto, porque, a diferencia de una clase, a struct always has a default parameterless constructor; dada la forma en que se escribe su estructura, no se puede evitar un valor de 0:

Las estructuras no pueden contener constructores explícitos sin parámetros. Los miembros de Struct se inicializan automáticamente a sus valores predeterminados.

1

En este caso, es preferible una clase inmutable, ya que el estado predeterminado no es válido. Es un poco más costoso en cuanto al uso de la memoria, pero eso no debería importar a menos que uses un gran número de estos. Realmente, sin embargo, una restricción de 'número no cero' probablemente se maneje mejor en un nivel contractual para cada método, en lugar de ponerlo en una clase.

Si realmente desea hacer cumplir el contrato, coloque la excepción en el valor getter en lugar del constructor. Entonces el contrato es que lanzará una excepción si alguna vez contiene un valor de 0; el único beneficio real aquí es que nunca usas silenciosamente un valor cero. La desventaja es que ahora tiene una comparación cada vez que usa el valor.

1

Aunque no se puede conseguir exactamente lo que quiere usted podría validar en el captador:

public int Value 
{ 
    get 
    { 
     if (_value == 0) 
      throw new InvalidOperationException("Use of uninitialized struct"); 
     return _value; 
    } 
} 
Cuestiones relacionadas