creo que esta cuestión es un caso específico de una pregunta más general sobre la creación de la seguridad de tipos a través de una aplicación de C#. Mi ejemplo aquí es con 2 tipos de datos: precios y pesos. Tienen diferentes unidades de medida, por lo que nunca se debe tratar de asignar un precio a un peso o viceversa. Ambos debajo de las cubiertas son valores realmente decimales. (Estoy ignorando el hecho de que podría haber conversiones como libras por kilogramo, etc.). Esta misma idea podría aplicarse a cadenas con tipos específicos como EmailAddress y UserLastName.
Con un poco de código de la placa de la caldera uno puede hacer una conversión explícita o conversiones implícitas hacia adelante y hacia atrás entre los tipos específicos: Precio y Peso, y el tipo subyacente Decimal.
public class Weight
{
private readonly Decimal _value;
public Weight(Decimal value)
{
_value = value;
}
public static explicit operator Weight(Decimal value)
{
return new Weight(value);
}
public static explicit operator Decimal(Weight value)
{
return value._value;
}
};
public class Price {
private readonly Decimal _value;
public Price(Decimal value) {
_value = value;
}
public static explicit operator Price(Decimal value) {
return new Price(value);
}
public static explicit operator Decimal(Price value)
{
return value._value;
}
};
Con las anulaciones de operador "explícitas", uno obtiene un conjunto más restrictivo de cosas que uno puede hacer con estas clases. Tiene que ser un caso manual cada vez que cambie de un tipo a otro. Por ejemplo:
public void NeedsPrice(Price aPrice)
{
}
public void NeedsWeight(Weight aWeight)
{
}
public void NeedsDecimal(Decimal aDecimal)
{
}
public void ExplicitTest()
{
Price aPrice = (Price)1.23m;
Decimal aDecimal = 3.4m;
Weight aWeight = (Weight)132.0m;
// ok
aPrice = (Price)aDecimal;
aDecimal = (Decimal)aPrice;
// Errors need explicit case
aPrice = aDecimal;
aDecimal = aPrice;
//ok
aWeight = (Weight)aDecimal;
aDecimal = (Decimal) aWeight;
// Errors need explicit cast
aWeight = aDecimal;
aDecimal = aWeight;
// Errors (no such conversion exists)
aPrice = (Price)aWeight;
aWeight = (Weight)aPrice;
// Ok, but why would you ever do this.
aPrice = (Price)(Decimal)aWeight;
aWeight = (Weight)(Decimal)aPrice;
NeedsPrice(aPrice); //ok
NeedsDecimal(aPrice); //error
NeedsWeight(aPrice); //error
NeedsPrice(aDecimal); //error
NeedsDecimal(aDecimal); //ok
NeedsWeight(aDecimal); //error
NeedsPrice(aWeight); //error
NeedsDecimal(aWeight); //error
NeedsWeight(aWeight); //ok
}
Simplemente cambiar los "explícitas" operadores a los operadores "implícitas" mediante la sustitución de las palabras "explícito" con "implícita" en el código, se puede convertir de ida y vuelta a la clase decimal subyacente sin ningún trabajo extra. Esto hace que el Precio y el Peso se comporten más como un Decimal, pero aún no puede cambiar un Precio por un Peso. Este suele ser el nivel de seguridad que estoy buscando.
public void ImplicitTest()
{
Price aPrice = 1.23m;
Decimal aDecimal = 3.4m;
Weight aWeight = 132.0m;
// ok implicit cast
aPrice = aDecimal;
aDecimal = aPrice;
// ok implicit cast
aWeight = aDecimal;
aDecimal = aWeight;
// Errors
aPrice = aWeight;
aWeight = aPrice;
NeedsPrice(aPrice); //ok
NeedsDecimal(aPrice); //ok
NeedsWeight(aPrice); //error
NeedsPrice(aDecimal); //ok
NeedsDecimal(aDecimal); //ok
NeedsWeight(aDecimal); //ok
NeedsPrice(aWeight); //error
NeedsDecimal(aWeight); //ok
NeedsWeight(aWeight); //ok
}
Al hacerlo por cadena en lugar de decimal. Me gusta la idea que la respuesta de Thorarin de comprobar nulo y pasar nulo en la conversión. p.ej.
public static implicit operator EMailAddress(string address)
{
// Make
// EmailAddress myvar=null
// and
// string aNullString = null;
// EmailAddress myvar = aNullString;
// give the same result.
if (address == null)
return null;
return new EMailAddress(address);
}
para conseguir estas clases de trabajo como claves para las colecciones de diccionarios, también tendrá que aplicar iguales, GetHashCode, == operador, y el operador! =
Para hacer todo esto más fácil, hice una clase ValueType que puedo extender, la clase ValueType llama al tipo de base para todo menos para los operadores de conversión.
Si se ve como una cadena, debe ser una cadena. No estoy seguro de que sea posible, e incluso es una buena idea. – zerkms
¿La clase de cadena es defectuosa? –
Nadie esperaría ver ese tipo de cosas. Solo crea tu propia clase. –