it-swarm.com.de

Entspricht typedef in C #

Gibt es ein typedef-Äquivalent in C # oder eine Möglichkeit, ein ähnliches Verhalten zu erzielen? Ich habe ein bisschen gegoogelt, aber überall, wo ich hinschaue, scheint das negativ zu sein. Momentan habe ich eine Situation ähnlich der folgenden:

class GenericClass<T> 
{
    public event EventHandler<EventData> MyEvent;
    public class EventData : EventArgs { /* snip */ }
    // ... snip
}

Nun, ein Raketenwissenschaftler braucht nicht, um herauszufinden, dass dies sehr schnell zu viel Tipparbeit führen kann (Entschuldigung für das schreckliche Wortspiel), wenn er versucht, einen Handler für dieses Ereignis zu implementieren. Es würde so etwas wie das enden:

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();
}

Außer, dass ich in meinem Fall bereits einen komplexen Typ verwendet habe, nicht nur einen int. Es wäre schön, wenn es möglich wäre, dies ein wenig zu vereinfachen ...

Bearbeiten: dh. Vielleicht müssen Sie den EventHandler tippen, anstatt ihn neu definieren zu müssen, um ein ähnliches Verhalten zu erzielen.

295

Nein, es gibt kein echtes Äquivalent zu typedef. Sie können 'using'-Direktiven in einer Datei verwenden, z.

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

dies wirkt sich jedoch nur auf diese Quelldatei aus. In C und C++ habe ich die Erfahrung gemacht, dass typedef normalerweise in .h-Dateien verwendet wird, die weit verbreitet sind. Daher kann ein einzelnes typedef für ein ganzes Projekt verwendet werden. Diese Möglichkeit gibt es in C # nicht, da es in C # keine #include - Funktionalität gibt, mit der Sie die Anweisungen using aus einer Datei in eine andere einbinden können.

Glücklicherweise enthält das Beispiel does eine fix-implizite Methodengruppenkonvertierung. Sie können Ihre Event-Abonnement-Zeile folgendermaßen ändern:

gcInt.MyEvent += gcInt_MyEvent;

:)

317
Jon Skeet

Jon gab wirklich eine nette Lösung, ich wusste nicht, dass Sie das tun können!

Manchmal habe ich von der Klasse geerbt und ihre Konstruktoren erstellt. Z.B.

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

Nicht die beste Lösung (es sei denn, Ihre Assembly wird von anderen Personen verwendet), aber es funktioniert.

36

Wenn Sie wissen, was Sie tun, können Sie eine Klasse mit impliziten Operatoren definieren, die zwischen der Alias-Klasse und der tatsächlichen Klasse konvertiert werden.

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 };
    }
}

Ich stimme dem nicht zu und habe so etwas noch nie benutzt, aber das könnte unter bestimmten Umständen funktionieren.

18
palswim

C # unterstützt einige geerbte Kovarianz für Ereignisdelegierte, also eine Methode wie die folgende:

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

Kann zum Abonnieren Ihrer Veranstaltung verwendet werden, ohne dass eine explizite Besetzung erforderlich ist

gcInt.MyEvent += LowestCommonHander;

Sie können sogar Lambda-Syntax verwenden und die Intellisense-Funktion wird für Sie ausgeführt:

gcInt.MyEvent += (sender, e) =>
{
    e. //you'll get correct intellisense here
};
6
Keith

Ich denke, es gibt kein typedef. Sie können nur einen bestimmten Delegattyp anstelle des generischen in der GenericClass definieren, d. H.

public delegate GenericHandler EventHandler<EventData>

Dies würde es kürzer machen. Aber was ist mit dem folgenden Vorschlag:

Verwenden Sie Visual Studio. Auf diese Weise, wenn Sie tippten

gcInt.MyEvent += 

es stellt bereits die vollständige Ereignishandlersignatur von Intellisense bereit. Drücken Sie die Tabulatortaste und es ist da. Akzeptieren Sie den generierten Handlernamen oder ändern Sie ihn, und drücken Sie erneut die Tabulatortaste, um den Handler-Stub automatisch zu generieren.

5
OregonGhost

Sie können eine Open-Source-Bibliothek und ein NuGet-Paket namens LikeType verwenden, das ich erstellt habe und das Ihnen das GenericClass<int> Verhalten, das Sie suchen.

Der Code würde so aussehen:

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
Matt Klein

Hier ist der Code dafür, viel Spaß! Ich habe die Anweisung "using" in die Namespace-Zeile 106 http://referencesource.Microsoft.com) von dotNetReference eingegeben /#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"};
        }
    }
}
2
shakram02

Sowohl in C++ als auch in C # fehlen einfache Möglichkeiten, einen neuen -Typ zu erstellen, der semantisch mit einem vorhandenen Typ identisch ist. Ich finde solche 'typedefs' für die typsichere Programmierung absolut notwendig und es ist eine echte Schande, dass c # sie nicht eingebaut hat. Der Unterschied zwischen void f(string connectionID, string username) und void f(ConID connectionID, UserName username) ist offensichtlich ...

(Ähnliches können Sie in C++ mit boost in BOOST_STRONG_TYPEDEF erreichen.)

Es mag verlockend sein, Vererbung zu verwenden, aber das hat einige wesentliche Einschränkungen:

  • es funktioniert nicht für primitive Typen
  • der abgeleitete Typ kann immer noch in den ursprünglichen Typ umgewandelt werden, dh wir können ihn an eine Funktion senden, die unseren ursprünglichen Typ empfängt. Dies macht den gesamten Zweck zunichte
  • wir können nicht von versiegelten Klassen ableiten (und das heißt, viele .NET-Klassen sind versiegelt)

Die einzige Möglichkeit, in C # ein ähnliches Ergebnis zu erzielen, besteht darin, unseren Typ in einer neuen Klasse zu komponieren:

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) => obj is TDerived o && Composed.Equals(o.Composed); 
  public bool Equals(SomeTypeTypeDefo) => object.Equals(this, o);

  // proxy the methods we want
  public void Method() => Composed.Method();
}

Während dies funktionieren wird, ist es nur für einen Typedef sehr ausführlich. Außerdem haben wir ein Problem mit der Serialisierung (dh zu Json), da wir die Klasse über ihre Composed-Eigenschaft serialisieren möchten.

Im Folgenden finden Sie eine Hilfsklasse, die das "Curiously Recurring Template Pattern" verwendet, um dies zu vereinfachen:

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) => obj is Composer<TDerived, T> o && 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);
    }
  }

}

Mit Composer wird die obige Klasse einfach:

sealed Class SomeTypeTypeDef : Composer<SomeTypeTypeDef, SomeType> {
   public SomeTypeTypeDef(SomeType composed) : base(composed) {}

   // proxy the methods we want
   public void Method() => Composed.Method();
}

Außerdem wird SomeTypeTypeDef auf dieselbe Weise wie SomeType in Json serialisiert.

Das Erfordernis, Methoden zu vertreten, kann mühsam sein, ist aber auch ein Segen in der Tarnung. Als neuer Typ möchten wir oft nur ausgewählte Methoden auswählen und neue Methoden zu unserem "typedef" hinzufügen.

Hoffe das hilft !

2
kofifus

Die beste Alternative zu typedef, die ich in C # gefunden habe, ist using. Zum Beispiel kann ich die Float-Genauigkeit über Compiler-Flags mit folgendem Code steuern:

#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif

Leider müssen Sie dies am Anfang von jeder Datei platzieren, wo Sie real_t Verwenden. Derzeit ist es nicht möglich, einen globalen Namespace-Typ in C # zu deklarieren.

0
Aaron Franke