Equivalente de typedef en C#
(9)
Aquí está el código para eso, ¡disfrútalo! Tomé eso del tipo dotNetReference, la declaración "using" dentro de la línea de espacio de nombres 106 http://referencesource.microsoft.com/#mscorlib/microsoft/win32/win32native.cs
using System;
using System.Collections.Generic;
namespace UsingStatement
{
using Typedeffed = System.Int32;
using TypeDeffed2 = List<string>;
class Program
{
static void Main(string[] args)
{
Typedeffed numericVal = 5;
Console.WriteLine(numericVal++);
TypeDeffed2 things = new TypeDeffed2 { "whatever"};
}
}
}
¿Hay un typedef equivalente en C #, o de alguna manera para obtener algún tipo de comportamiento similar? He hecho un poco de googlear, pero por todas partes que veo parece ser negativo. Actualmente tengo una situación similar a la siguiente:
class GenericClass<T>
{
public event EventHandler<EventData> MyEvent;
public class EventData : EventArgs { /* snip */ }
// ... snip
}
Ahora, no hace falta ser un científico espacial para darse cuenta de que esto puede llevar muy rápidamente a escribir mucho (disculpas por el horrible juego de palabras) cuando se trata de implementar un controlador para ese evento. Terminaría siendo algo como esto:
GenericClass<int> gcInt = new GenericClass<int>;
gcInt.MyEvent += new EventHandler<GenericClass<int>.EventData>(gcInt_MyEvent);
// ...
private void gcInt_MyEvent(object sender, GenericClass<int>.EventData e)
{
throw new NotImplementedException();
}
Excepto, en mi caso, ya estaba usando un tipo complejo, no solo un int. Sería bueno si fuera posible simplificar esto un poco ...
Editar: es decir. tal vez escriba el EventHandler en lugar de tener que redefinirlo para obtener un comportamiento similar.
C # admite algunas covarianza heredadas para los delegados de eventos, por lo que un método como este:
void LowestCommonHander( object sender, EventArgs e ) { ... }
Puede utilizarse para suscribirse a su evento, no se requiere una conversión explícita
gcInt.MyEvent += LowestCommonHander;
Incluso puedes usar la sintaxis lambda y la inteligencia se hará por ti:
gcInt.MyEvent += (sender, e) =>
{
e. //you''ll get correct intellisense here
};
Considero que typedefs es totalmente esencial para la programación de tipos seguros y es una verdadera lástima que c # no los tenga integrados. La diferencia entre void f(string connectionID, string username)
y void f(ConID connectionID, UserName username)
es obvia ...
Puede ser tentador usar la herencia, pero eso tiene algunas limitaciones importantes:
- no funcionará para tipos primitivos
- el tipo derivado aún puede ser convertido al tipo original, es decir, podemos enviarlo a una función que recibe nuestro tipo original, esto anula todo el propósito
- no podemos derivar de clases selladas (y es decir, muchas clases .NET están selladas)
La única forma de lograr algo similar en C # es componer nuestro tipo en una nueva clase:
Class SomeType {
public void Method() { .. }
}
sealed Class SomeTypeTypeDef {
public SomeTypeTypeDef(SomeType composed) { this.Composed = composed; }
private SomeType Composed { get; }
public override string ToString() => Composed.ToString();
public override int GetHashCode() => HashCode.Combine(Composed);
public override bool Equals(object obj) { var o = obj as SomeTypeTypeDef; return o is null ? false : Composed.Equals(o.Composed); }
public bool Equals(SomeTypeTypeDefo) => object.Equals(this, o);
// proxy the methods we want
public void Method() => Composed.Method();
}
Si bien esto funcionará, es muy detallado solo para un typedef. Además, tenemos un problema con la serialización (es decir, con Json), ya que queremos serializar la clase a través de su propiedad Composed.
A continuación, se encuentra una clase de ayuda que utiliza el "Patrón de plantilla curiosamente recurrente" para hacer esto mucho más simple:
namespace Typedef {
[JsonConverter(typeof(JsonCompositionConverter))]
public abstract class Composer<TDerived, T> : IEquatable<TDerived> where TDerived : Composer<TDerived, T> {
protected Composer(T composed) { this.Composed = composed; }
protected Composer(TDerived d) { this.Composed = d.Composed; }
protected T Composed { get; }
public override string ToString() => Composed.ToString();
public override int GetHashCode() => HashCode.Combine(Composed);
public override bool Equals(object obj) { var o = obj as TDerived; return o is null ? false : Composed.Equals(o.Composed); }
public bool Equals(TDerived o) => object.Equals(this, o);
}
class JsonCompositionConverter : JsonConverter {
static FieldInfo GetCompositorField(Type t) {
var fields = t.BaseType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy);
if (fields.Length!=1) throw new JsonSerializationException();
return fields[0];
}
public override bool CanConvert(Type t) {
var fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy);
return fields.Length == 1;
}
// assumes Compositor<T> has either a constructor accepting T or an empty constructor
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
while (reader.TokenType == JsonToken.Comment && reader.Read()) { };
if (reader.TokenType == JsonToken.Null) return null;
var compositorField = GetCompositorField(objectType);
var compositorType = compositorField.FieldType;
var compositorValue = serializer.Deserialize(reader, compositorType);
var ctorT = objectType.GetConstructor(new Type[] { compositorType });
if (!(ctorT is null)) return Activator.CreateInstance(objectType, compositorValue);
var ctorEmpty = objectType.GetConstructor(new Type[] { });
if (ctorEmpty is null) throw new JsonSerializationException();
var res = Activator.CreateInstance(objectType);
compositorField.SetValue(res, compositorValue);
return res;
}
public override void WriteJson(JsonWriter writer, object o, JsonSerializer serializer) {
var compositorField = GetCompositorField(o.GetType());
var value = compositorField.GetValue(o);
serializer.Serialize(writer, value);
}
}
}
Con Composer la clase anterior se convierte simplemente en:
sealed Class SomeTypeTypeDef : Composer<SomeTypeTypeDef, SomeType> {
public SomeTypeTypeDef(SomeType composed) : base(composed) {}
// proxy the methods we want
public void Method() => Composed.Method();
}
Y además, SomeTypeTypeDef
se serializará a Json de la misma manera que lo hace SomeType
.
Tenga en cuenta que si implementamos ==
y !=
En SomeTypeTypeDef
el compilador advertirá que falta Equals
aunque se herede correctamente, podemos usar de forma segura:
#pragma warning disable CS0660, CS0661
para deshabilitar estas advertencias.
La necesidad de usar métodos proxy puede ser una molestia, pero también es una bendición disfrazada, ya que al ser un tipo nuevo, a menudo solo queremos fw métodos seleccionados y agregar nuevos a nuestro "typedef"
Espero que esto ayude !
Creo que no hay typedef. Solo podría definir un tipo de delegado específico en lugar de uno genérico en GenericClass, es decir,
public delegate GenericHandler EventHandler<EventData>
Esto lo haría más corto. Pero ¿qué pasa con la siguiente sugerencia:
Utilice Visual Studio. De esta manera, cuando escribiste
gcInt.MyEvent +=
ya proporciona la firma completa del controlador de eventos de Intellisense. Presiona TAB y está ahí. Acepte el nombre del controlador generado o cámbielo, y luego presione TAB nuevamente para generar automáticamente el talón del controlador.
Jon realmente dio una buena solución, ¡no sabía que podías hacer eso!
A veces, a lo que recurrí heredaba de la clase y creaba sus constructores. P.ej
public class FooList : List<Foo> { ... }
No es la mejor solución (a menos que su ensamblaje sea usado por otras personas), pero funciona.
La mejor alternativa para typedef
que he encontrado en C # está using
. Por ejemplo, puedo controlar la precisión de flotación a través de los indicadores del compilador con este código:
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
Desafortunadamente, requiere que real_t
esto en la parte superior de cada archivo donde usas real_t
. Actualmente no hay forma de declarar un tipo de espacio de nombres global en C #.
No, no hay un verdadero equivalente de typedef. Puede usar las directivas ''usando'' dentro de un archivo, por ejemplo,
using CustomerList = System.Collections.Generic.List<Customer>;
pero eso solo afectará ese archivo fuente. En C y C ++, mi experiencia es que typedef
se usa generalmente en archivos .h que se incluyen ampliamente, por lo que una sola typedef
se puede usar en un proyecto completo. Esa capacidad no existe en C #, porque no hay ninguna funcionalidad #include
en C # que le permita incluir las directivas de using
de un archivo en otro.
Afortunadamente, el ejemplo que da tiene una conversión de grupo de métodos implícita de reparación. Puede cambiar su línea de suscripción de eventos a solo:
gcInt.MyEvent += gcInt_MyEvent;
:)
Puede usar una biblioteca de código abierto y un paquete NuGet llamado LikeType que creé que le dará el GenericClass<int>
que está buscando.
El código se vería así:
public class SomeInt : LikeType<int>
{
public SomeInt(int value) : base(value) { }
}
[TestClass]
public class HashSetExample
{
[TestMethod]
public void Contains_WhenInstanceAdded_ReturnsTrueWhenTestedWithDifferentInstanceHavingSameValue()
{
var myInt = new SomeInt(42);
var myIntCopy = new SomeInt(42);
var otherInt = new SomeInt(4111);
Assert.IsTrue(myInt == myIntCopy);
Assert.IsFalse(myInt.Equals(otherInt));
var mySet = new HashSet<SomeInt>();
mySet.Add(myInt);
Assert.IsTrue(mySet.Contains(myIntCopy));
}
}
Si sabe lo que está haciendo, puede definir una clase con operadores implícitos para convertir entre la clase de alias y la clase real.
class TypedefString // Example with a string "typedef"
{
private string Value = "";
public static implicit operator string(TypedefString ts)
{
return ((ts == null) ? null : ts.Value);
}
public static implicit operator TypedefString(string val)
{
return new TypedefString { Value = val };
}
}
Realmente no lo apoyo y nunca he usado algo como esto, pero esto probablemente podría funcionar para algunas circunstancias específicas.