Equivalente de typedef en C#


¿Hay un equivalente de typedef en C#, o de alguna manera para obtener algún tipo de comportamiento similar? He buscado en Google, pero donde quiera que mire 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 se necesita un científico de cohetes para darse cuenta de que esto puede conducir muy rápidamente a una gran cantidad de escritura (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 case, ya estaba usando un tipo complejo, no solo un int. Sería bueno si fuera posible simplificar esto un poco...

Editar: ie. tal vez escribir el EventHandler en lugar de tener que redefinirlo para obtener un comportamiento similar.

 273
Author: bacar, 2008-10-02

9 answers

No, no hay un verdadero equivalente de typedef. Puede usar directivas 'using' dentro de un archivo, por ejemplo

using CustomerList = System.Collections.Generic.List<Customer>;

Pero eso solo afectará a ese archivo de origen. En C y C++, mi experiencia es que typedef se usa generalmente dentro .archivos h que se incluyen ampliamente, por lo que un único typedef se puede usar sobre todo un proyecto. Esa habilidad no existe en C#, porque no hay una funcionalidad #include en C # que le permita incluir las directivas using de un archivo en otro.

Afortunadamente, el ejemplo que das tiene una conversión de grupo de método implícito fix. Puedes cambiar tu línea de suscripción al evento a solo:

gcInt.MyEvent += gcInt_MyEvent;

:)

 292
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-18 14:41:15

Jon realmente dio una buena solución, ¡no sabía que podías hacer eso!

A veces lo que recurrí fue heredar de la clase y crear sus constructores. Por ejemplo,

public class FooList : List<Foo> { ... }

No es la mejor solución (a menos que su ensamblaje sea utilizado por otras personas), pero funciona.

 34
Author: Jonathan C Dickinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-18 17:19:36

Si sabe lo que está haciendo, puede definir una clase con operadores implícitos para convertir entre la clase 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 };
    }
}

En realidad no apruebo esto y nunca he usado algo como esto, pero esto probablemente podría funcionar para algunas circunstancias específicas.

 18
Author: palswim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-22 18:50:36

C # admite algunas covarianzas heredadas para delegados de eventos, por lo que un método como este:

void LowestCommonHander( object sender, EventArgs e ) { ... } 

Se puede usar para suscribirse a su evento, no se requiere un cast explícito

gcInt.MyEvent += LowestCommonHander;

Incluso puede usar la sintaxis lambda y el intellisense se hará por usted:

gcInt.MyEvent += (sender, e) =>
{
    e. //you'll get correct intellisense here
};
 6
Author: Keith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-14 11:25:17

Creo que no hay typedef. Solo puede definir un tipo de delegado específico en lugar del genérico en la clase genérica, es decir,

public delegate GenericHandler EventHandler<EventData>

Esto lo haría más corto. Pero qué hay de 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 manejador.

 5
Author: OregonGhost,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-02 09:23:29

Puedes usar una biblioteca de código abierto y un paquete NuGet llamado LikeType que creé y que te dará el comportamiento GenericClass<int> que estás buscando.

El código se vería como:

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));
    }
}
 3
Author: Matt Klein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-13 17:07:30

Aquí está el código para ello, disfrutar!, Recogí eso de la referencia dotNetReference escriba la instrucción" using " dentro de la línea 106 del espacio de nombres 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"};
        }
    }
}
 3
Author: shakram02,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-03 03:49:59

Encuentro typedefs totalmente esencial para la programación segura de tipos y es una verdadera pena que c# no los tenga incorporados. La diferencia entre void f(string connectionID, string username) y void f(ConID connectionID, UserName username) es obvia ...

Puede ser tentador usar herencia, pero eso tiene algunas limitaciones importantes:

  • no funcionará para tipos primitivos
  • el tipo derivado todavía puede ser fundido al tipo original, es decir, podemos enviarlo a una función que recibe nuestro tipo original, esto derrota todo el propósito
  • nosotros no puede derivar de clases selladas (e ie muchas clases. NET están selladas)

La única manera de lograr algo similar en C# es componiendo 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, a Json) ya que queremos serializar la clase a través de su propiedad Compuesta.

A continuación se muestra una clase auxiliar que utiliza el "Patrón de Plantilla Curiosamente recurrente" para hacer esto 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:

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 el SomeTypeTypeDef se serializará a Json de la misma manera que SomeType lo hace.

Tenga en cuenta que si implementamos == y != en SomeTypeTypeDef el compilador advertirá que Equals falta a pesar de que se hereda correctamente, podemos usar con seguridad:

#pragma warning disable CS0660, CS0661

Para desactivar estas advertencias.

La necesidad de métodos proxy puede ser una molestia, pero también es una bendición disfrazada, al ser un nuevo tipo a menudo solo queremos fw métodos seleccionados y añadir otros nuevos a nuestro "typedef"

Espero que esto ayude !

 0
Author: kofifus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-23 05:02:50

La mejor alternativa a typedef que he encontrado en C# es using. Por ejemplo, puedo controlar float precision a través de indicadores de 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 coloque esto en la parte superior de cada archivo donde use real_t. Actualmente no hay forma de declarar un tipo de espacio de nombres global en C#.

 -1
Author: Aaron Franke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-30 06:25:03