C #의 typedef와 동일 MyEvent;

C #에 동등한 typedef가 있습니까, 아니면 어쨌든 비슷한 행동을 취하기 위해 어떻습니까? 인터넷 검색을 해봤지만 어디에서나 부정적인 것으로 보입니다. 현재 다음과 유사한 상황이 있습니다.

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

이제 로켓 과학자가 해당 이벤트에 대한 핸들러를 구현하려고 할 때 많은 타이핑 (끔찍한 말장난에 대한 사과)을 초래할 수 있음을 알아내는 데 로켓 과학자가 필요하지 않습니다. 결국 다음과 같이됩니다.

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

내 경우에는 이미 int뿐만 아니라 복잡한 유형을 사용하고 있습니다. 이것을 조금 단순화하는 것이 가능하다면 좋을 것입니다 …

편집 : 즉. 비슷한 동작을 얻기 위해 재정의 해야하는 대신 EventHandler를 typedefing 할 수 있습니다.



답변

아니요, typedef와 동등한 것은 없습니다. 하나의 파일 내에서 ‘using’지시문을 사용할 수 있습니다. 예 :

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

그러나 해당 소스 파일에만 영향을 미칩니다. C 및 C ++에서 필자의 경험은 typedef일반적으로 광범위하게 포함 된 .h 파일 내에서 사용되므로 typedef전체 프로젝트 에서 단일을 사용할 수 있다는 것입니다. C # #include에는 using한 파일 의 지시문을 다른 파일에 포함시킬 수 있는 기능이 없으므로 C #에는 해당 기능이 없습니다 .

다행스럽게도 여러분이 제공하는 예제 에는 암시 적 메소드 그룹 변환이라는 수정 사항 있습니다. 이벤트 구독 줄을 다음과 같이 변경할 수 있습니다.

gcInt.MyEvent += gcInt_MyEvent;

🙂


답변

존은 정말 좋은 해결책을 줬습니다.

때때로 내가 의지했던 것은 클래스에서 상속하고 생성자를 만드는 것이 었습니다. 예 :

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

최상의 솔루션은 아니지만 (다른 사람이 어셈블리를 사용하지 않는 한) 작동합니다.


답변

수행중인 작업을 알고 있으면 암시 적 연산자로 클래스를 정의하여 별칭 클래스와 실제 클래스 사이를 변환 할 수 있습니다.

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

나는 실제로 이것을 보증하지 않으며 이와 같은 것을 사용하지는 않았지만 이것은 특정 상황에서 효과적 일 수 있습니다.


답변

C #은 이벤트 대리자에 대해 상속 된 공분산을 지원하므로 다음과 같은 방법이 있습니다.

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

명시 적 캐스트가 필요하지 않은 이벤트를 구독하는 데 사용할 수 있습니다.

gcInt.MyEvent += LowestCommonHander;

람다 구문을 사용할 수도 있고 인텔리전스가 모두 수행됩니다.

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

답변

typedef가 없다고 생각합니다. GenericClass에서 제네릭 유형 대신 특정 델리게이트 유형 만 정의 할 수 있습니다.

public delegate GenericHandler EventHandler<EventData>

이것은 더 짧아 질 것입니다. 그러나 다음 제안은 어떻습니까?

Visual Studio를 사용하십시오. 이렇게하면 입력 할 때

gcInt.MyEvent += 

이미 Intellisense의 완전한 이벤트 핸들러 서명을 제공합니다. Tab을 누르면 거기에 있습니다. 생성 된 핸들러 이름을 승인하거나 변경 한 후 Tab 키를 다시 눌러 핸들러 스텁을 자동 생성하십시오.


답변

C ++과 C # 모두 기존 형식과 의미가 동일한 형식 을 만드는 쉬운 방법이 없습니다 . 나는 타입 안전 프로그래밍에 꼭 필요한 ‘typedefs’를 발견하고 실제 수치는 c #에 내장되어 있지 않습니다. 의 차이 void f(string connectionID, string username)로는 void f(ConID connectionID, UserName username)분명하다 …

(BOOST_STRONG_TYPEDEF를 높이면 C ++에서 비슷한 것을 얻을 수 있습니다)

상속을 사용하고 싶을 수도 있지만 몇 가지 중요한 제한이 있습니다.

  • 기본 유형에는 작동하지 않습니다
  • 파생 된 유형은 여전히 ​​원래 유형으로 캐스트 될 수 있습니다. 즉, 원래 유형을받는 함수에 보낼 수 있습니다.
  • 봉인 된 클래스에서 파생 될 수 없습니다 (즉, 많은 .NET 클래스가 봉인 됨)

C #에서 비슷한 것을 달성하는 유일한 방법은 새 클래스에서 형식을 작성하는 것입니다.

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

이것이 효과가 있지만 typedef에 대해서는 매우 장황합니다. 또한 Composed 속성을 통해 클래스를 직렬화하려는 경우 직렬화 (예 : Json)에 문제가 있습니다.

아래는 “재귀 적으로 반복되는 템플릿 패턴”을 사용하여 훨씬 간단하게 만드는 도우미 클래스입니다.

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

}

Composer를 사용하면 위 클래스가 간단 해집니다.

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

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

또한 SomeTypeTypeDef의지는 동일한 방식으로 Json으로 직렬화됩니다 SomeType.

도움이 되었기를 바랍니다 !


답변

오픈 소스 라이브러리와 내가 만든 LikeType 이라는 NuGet 패키지 를 사용하면 원하는 GenericClass<int>동작을 얻을 수 있습니다 .

코드는 다음과 같습니다.

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