Odpowiednik typedef w C#

Czy w C# jest odpowiednik typedef, czy może coś podobnego? Trochę wygooglowałem, ale wszędzie, gdzie zaglądam, wydaje mi się, że jest negatywna. Obecnie mam sytuację podobną do następującej:

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

Teraz nie potrzeba naukowca rakietowego, aby zorientować się, że może to bardzo szybko prowadzić do wielu typowań (przepraszam za okropną grę słów) podczas próby wdrożenia obsługi tego wydarzenia. Skończyło by się tak:

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

Poza tym, że w moim case, ja już używałem typu złożonego, a nie tylko int. Byłoby miło, gdyby można było to trochę uprościć...

Edit: ie. być może wpisz określenie EventHandler zamiast konieczności redefiniowania go, aby uzyskać podobne zachowanie.

 273
Author: bacar, 2008-10-02

9 answers

Nie, Nie ma prawdziwego odpowiednika typedef. Możesz użyć dyrektyw 'using' w jednym pliku, np.

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

Ale to wpłynie tylko na ten plik źródłowy. W C i c++, moje doświadczenie jest takie, że typedef jest zwykle używany wewnątrz .pliki h, które są szeroko uwzględniane - więc pojedynczy typedef może być używany w całym projekcie. Ta zdolność nie istnieje w C#, ponieważ nie ma w C# funkcji #include, która pozwalałaby na dołączenie dyrektyw using z jednego pliku w kolejny.

Na szczęście, przykład, który podałeś mA konwersję grupy metod fix - implicit. Możesz zmienić linię subskrypcji wydarzenia NA just:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-18 14:41:15

Jon naprawdę dał fajne rozwiązanie, nie wiedziałem, że możesz to zrobić!

Czasami uciekałem się do dziedziczenia po klasie i tworzenia jej konstruktorów. Np.

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

Nie jest to najlepsze rozwiązanie (chyba, że twój zespół jest używany przez inne osoby), ale działa.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-18 17:19:36

Jeśli wiesz, co robisz, możesz zdefiniować klasę z operatorami niejawnymi do konwersji między klasą aliasu a klasą rzeczywistą.

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

Nie popieram tego i nigdy nie używałem czegoś takiego, ale to prawdopodobnie zadziała w pewnych szczególnych okolicznościach.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-22 18:50:36

C # obsługuje pewną dziedziczoną kowariancję dla delegatów zdarzeń, więc metoda taka jak Ta:

void LowestCommonHander( object sender, EventArgs e ) { ... } 
Może być użyty do zapisania się na wydarzenie, bez wyraźnej obsady
gcInt.MyEvent += LowestCommonHander;

Możesz nawet użyć składni lambda, a intellisense będzie wykonane za Ciebie:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-14 11:25:17

Myślę, że nie ma typedef. Możesz zdefiniować tylko określony typ delegata zamiast ogólnego w GenericClass, tzn.

public delegate GenericHandler EventHandler<EventData>
To by go skróciło. Ale co z następującą sugestią:

Użyj Visual Studio. W ten sposób, gdy wpisujesz

gcInt.MyEvent += 

Dostarcza już pełną sygnaturę obsługi zdarzeń z Intellisense. Naciśnij TAB i jest tam. Zaakceptuj wygenerowaną nazwę obsługi lub zmień ją, a następnie naciśnij TAB ponownie, aby automatycznie wygenerować stub Handlera.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-02 09:23:29

Możesz użyć biblioteki open source i pakietu NuGet o nazwie Jaketype, który stworzyłem, który da ci GenericClass<int> zachowanie, którego szukasz.

Kod wyglądałby następująco:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-13 17:07:30

Oto kod do tego, enjoy!, Odebrałem to z dotNetReference wpisz instrukcję "using" wewnątrz linii 106 przestrzeni nazw 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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-03 03:49:59

Uważam, że typedefs są całkowicie niezbędne do programowania bezpiecznego dla typów i szkoda, że c# nie ma ich wbudowanych. Różnica między void f(string connectionID, string username) a void f(ConID connectionID, UserName username) jest oczywista ...

Używanie dziedziczenia może być kuszące, ale to ma pewne główne ograniczenia:

  • nie będzie działać dla prymitywnych typów
  • Typ Pochodny może być nadal przypisany do typu oryginalnego, tzn. możemy go wysłać do funkcji odbierającej nasz typ Oryginalny, co pokonuje cały cel
  • we nie można wywodzić się z klas zamkniętych (tzn. wiele klas. NET jest zamkniętych)

Jedynym sposobem osiągnięcia podobnej rzeczy w C# jest skomponowanie naszego typu w nowej klasie:

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

Podczas gdy to zadziała, jest bardzo słowne dla tylko typedef. Dodatkowo mamy problem z serializacją (tj. do Json), ponieważ chcemy serializować klasę poprzez jej właściwość Composed.

Poniżej znajduje się Klasa pomocnicza, która używa "ciekawie powtarzającego się wzorca szablonów", aby to zrobić prostsze:

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

}

Z Composerem powyższa klasa staje się po prostu:

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

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

I dodatkowo SomeTypeTypeDef będzie serializował się do Json w taki sam sposób, jak robi to SomeType.

Zauważ, że jeśli zaimplementujemy == i != na SomeTypeTypeDef kompilator ostrzega, że brakuje Equals, mimo że jest poprawnie dziedziczony, możemy bezpiecznie używać:

#pragma warning disable CS0660, CS0661

Aby wyłączyć te ostrzeżenia.

Potrzeba zastępowania metod może być kłopotliwa, ale jest również błogosławieństwem w ukryciu, będąc nowym typem często chcemy tylko fw wybrane metody i dodać nowe do naszego "typedef"

Mam nadzieję, że to pomoże !

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-23 05:02:50

Najlepszą alternatywą dla typedef jaką znalazłem w C# jest using. Na przykład, mogę kontrolować precyzję float za pomocą flag kompilatora z tym kodem:

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

Niestety, wymaga to umieszczenia tego na górze KAŻDEGO pliku, w którym używasz real_t. Obecnie nie ma możliwości zadeklarowania typu globalnej przestrzeni nazw w 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/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-30 06:25:03