studio - ¿Hay alguna manera de definir los alias de tipo C#de tipos primitivos existentes como `string` o` int`?
tipos de funciones en c# (7)
Quizás estoy demostrando mi ignorancia de algunas de las aplicaciones más utilizadas de C # o de .NET framework, pero me gustaría saber si existe una forma nativa para crear un alias de tipo como EmailAddress
que alias string
pero que pueda ampliar ¿Con mis propios métodos como bool Validate()
?
Sé de la using x = Some.Type;
los alias, pero estos no son globales ni proporcionan seguridad de tipo, es decir, se podría intercambiar una string
ordinaria por el alias de uso en el archivo actual. Me gustaría que mi dirección de EmailAddress
fuera de su propio tipo, independiente y no intercambiable con el tipo de string
que sombrea.
Mi solución actual es generar public sealed partial EmailAddress : IEquatable<EmailAddress>, IXmlSerializable
clases public sealed partial EmailAddress : IEquatable<EmailAddress>, IXmlSerializable
con una plantilla T4 que genere los operadores de conversión de cadena implícita y otras cosas similares. Esto está bien conmigo por ahora y me da mucha flexibilidad, pero en el fondo de mi mente parece tonto que tenga que generar cantidades tan abundantes de código repetitivo para hacer algo tan simple como crear un alias de tipo fuerte.
Tal vez esto no sea posible más que con la generación de código, pero tengo curiosidad de que otros hayan intentado algo similar con sus diseños y con sus experiencias. Si nada más, quizás esto podría servir como un buen caso de uso para tal característica de alias en una futura versión hipotética de C #. ¡Gracias!
EDITAR: El valor real que quiero obtener de esto es poder obtener seguridad de tipos con tipos primitivos que representan diferentes tipos / formatos de datos. Por ejemplo, una dirección de SocialSecurityNumber
y un número de SocialSecurityNumber
y un número de PhoneNumber
, todos los cuales utilizan string
como su tipo subyacente, pero que no son tipos intercambiables en sí mismos. Creo que esto le da un código mucho más legible y autodocumentado, sin mencionar los beneficios adicionales de más posibilidades de sobrecarga de métodos que son menos ambiguas.
¿La clase System.Net.Mail.MailAddress
ajusta a sus necesidades, o al menos a "ayuda"?
EDITAR: No es explícitamente IEquatable o ISerializable, pero puede agregarlos fácilmente en su propio envoltorio.
Algunos antecedentes sobre por qué la string
está sellada:
De http://www.code-magazine.com/Article.aspx?quickid=0501091 :
Rory: Hey Jay, ¿te importa si te hago un par de preguntas? Ya tengo curiosidad por algunas cosas. En primer lugar, y esto se planteó en uno de los eventos de MSDN que hice esta semana, ¿por qué está sellada la cadena? Nota: para los programadores de VB.NET, Sellado = No Heredable.
Jay: Porque hacemos muchos trucos de magia en String para intentar asegurarnos de que podemos optimizar cosas como comparaciones, para que sean lo más rápido posible. Por lo tanto, estamos robando bits de punteros y otras cosas allí para marcar las cosas. Solo para darle un ejemplo, y no sabía esto cuando comencé, pero si una Cadena tiene un guión o un apóstrofe en ella [entonces] se clasifica de manera diferente a como si solo tuviera texto, y el algoritmo de clasificación Si tiene un guión o un apóstrofe, si está realizando una clasificación global, es bastante complicado, por lo que en realidad marcamos si la cadena tiene ese tipo de comportamiento.
Rory: Entonces, lo que estás diciendo es que en el mundo de las cuerdas, si no sellas las cuerdas, habría mucho espacio para causar un gran estrago si la gente intentara crear una subclase.
Jay: Exactamente. Cambiaría todo el diseño del objeto, por lo que no podríamos jugar los trucos que jugamos a esa velocidad de recogida.
Aquí está el artículo de CodeProject que probablemente haya visto antes:
http://www.codeproject.com/KB/cs/expandSealed.aspx
Así que sí, el operador implícito es tu única solución.
Creo que quieres usar métodos de extensión. Le permiten extender una funcionalidad de clases sin crear un nuevo tipo derivado.
Hice esta clase para cubrir necesidades idénticas. Este es para el tipo "int" (también tengo uno para "cadena"):
public class NamedInt : IComparable<int>, IEquatable<int>
{
internal int Value { get; }
protected NamedInt() { }
protected NamedInt(int val) { Value = val; }
protected NamedInt(string val) { Value = Convert.ToInt32(val); }
public static implicit operator int (NamedInt val) { return val.Value; }
public static bool operator ==(NamedInt a, int b) { return a?.Value == b; }
public static bool operator ==(NamedInt a, NamedInt b) { return a?.Value == b?.Value; }
public static bool operator !=(NamedInt a, int b) { return !(a==b); }
public static bool operator !=(NamedInt a, NamedInt b) { return !(a==b); }
public bool Equals(int other) { return Equals(new NamedInt(other)); }
public override bool Equals(object other) {
if ((other.GetType() != GetType() && other.GetType() != typeof(string))) return false;
return Equals(new NamedInt(other.ToString()));
}
private bool Equals(NamedInt other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(Value, other.Value);
}
public int CompareTo(int other) { return Value - other; }
public int CompareTo(NamedInt other) { return Value - other.Value; }
public override int GetHashCode() { return Value.GetHashCode(); }
public override string ToString() { return Value.ToString(); }
}
Y consumirlo en tu caso:
public class MyStronglyTypedInt: NamedInt {
public MyStronglyTypedInt(int value) : base(value) {
// Your validation can go here
}
public static implicit operator MyStronglyTypedInt(int value) {
return new MyStronglyTypedInt(value);
}
public bool Validate() {
// Your validation can go here
}
}
Si necesita poder serializarlo (Newtonsoft.Json), avíseme y agregaré el código.
Parece que tienes al menos un C # knoledgde razonable, por lo que mi respuesta puede parecer estúpida, pero lo que quieres se llama "jerarquía de tipos" y los tipos que codificaron la clase String querían evitar que usaras esta "característica OO", así que hicieron String Clase sellada, por eso no podrás hacer lo que quieras. El mejor enfoque es este en el que estás ahora: crea tu propio tipo y una conversión implícita a String.
Si observa el .NET Framework System.Uri es el ejemplo más cercano que es similar a una dirección de correo electrónico. En .NET, el patrón es envolver algo en una clase y agregar restricciones de esa manera.
Agregar una escritura fuerte que agregue restricciones adicionales a tipos simples es una característica interesante del lenguaje que creo que tiene un lenguaje funcional. No puedo recordar el nombre del lenguaje que le permitiría agregar unidades dimensionales como pies a sus valores y hacer un análisis dimensional en sus ecuaciones para asegurar que las unidades coincidan.
Supongo que no entiendo por qué quiere tener tipos fuertes y conversión implícita de cadenas al mismo tiempo. Para mí, una descarta la otra.
Intenté resolver el mismo problema para ints (mencionas int en el título, pero no en la pregunta). Descubrí que declarar una enumeración te da un número entero seguro para el tipo que se debe convertir explícitamente de / a int.
Actualizar
Las enumeraciones pueden no estar destinadas a conjuntos abiertos, pero aún pueden usarse de esa manera. Este ejemplo es de un experimento de compilación para distinguir entre las columnas de ID de varias tablas en una base de datos:
enum ProcID { Unassigned = 0 }
enum TenderID { Unassigned = 0 }
void Test()
{
ProcID p = 0;
TenderID t = 0; <-- 0 is assignable to every enum
p = (ProcID)3; <-- need to explicitly convert
if (p == t) <-- operator == cannot be applied
t = -1; <-- cannot implicitly convert
DoProc(p);
DoProc(t); <-- no overloaded method found
DoTender(t);
}
void DoProc(ProcID p)
{
}
void DoTender(TenderID t)
{
}