web-dev-qa-db-ja.com

C#のtypedefと同等

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を再定義する必要はなく、EventHandlerを型定義します。

290

いいえ、typedefに相当するものはありません。 1つのファイル内で「using」ディレクティブを使用できます。

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

しかし、それはそのソースファイルにのみ影響します。 CおよびC++での私の経験では、typedefは通常、広く含まれている.hファイル内で使用されるため、プロジェクト全体で単一のtypedefを使用できます。 C#には、あるファイルのusingディレクティブを別のファイルに含めることができる#include機能がないため、この機能はC#には存在しません。

幸いなことに、doesを指定した例には修正があります-暗黙的なメソッドグループ変換。イベントサブスクリプションの行を次のように変更できます。

gcInt.MyEvent += gcInt_MyEvent;

:)

312
Jon Skeet

ジョンは本当に素敵なソリューションを提供しました、あなたがそれをすることができるとは知りませんでした!

時々私が頼ったのは、クラスから継承し、そのコンストラクタを作成することでした。例えば。

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

(アセンブリが他の人に使用されない限り)最良の解決策ではありませんが、機能します。

35

実行内容がわかっている場合は、暗黙的な演算子を使用してクラスを定義し、エイリアスクラスと実際のクラスを変換できます。

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

私は実際にこれを推奨しておらず、このようなものを使用したことはありませんが、これはおそらく特定の状況で機能する可能性があります。

18
palswim

C#は、イベントデリゲートの継承された共分散をサポートしているため、次のようなメソッドです。

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

イベントをサブスクライブするために使用できます。明示的なキャストは不要です

gcInt.MyEvent += LowestCommonHander;

ラムダ構文を使用することもでき、インテリセンスはすべて行われます:

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

Typedefがないと思います。 GenericClassのジェネリックタイプではなく、特定のデリゲートタイプのみを定義できます。

public delegate GenericHandler EventHandler<EventData>

これにより短くなります。しかし、次の提案についてはどうですか:

Visual Studioを使用します。このように、入力したとき

gcInt.MyEvent += 

intellisenseからの完全なイベントハンドラシグネチャを既に提供しています。 Tabキーを押すと、そこにあります。生成されたハンドラー名を受け入れるか変更してから、もう一度Tabキーを押してハンドラースタブを自動生成します。

5
OregonGhost

あなたが探しているGenericClass<int>の振る舞いを提供する LikeType と呼ばれるオープンソースライブラリとNuGetパッケージを使用できます。

コードは次のようになります。

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

ここにそのコードがあります、お楽しみください!名前空間行106http://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"};
        }
    }
}
2
shakram02

C++とC#の両方に、既存の型と意味的に同一のnew型を作成する簡単な方法がありません。このような「typedef」はタイプセーフプログラミングに不可欠であり、本当の恥ずかしい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)に問題があります。

以下は、「Curiously Recurring Template Pattern」を使用してこれをより簡単にするヘルパークラスです。

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は、SomeTypeと同じ方法でJsonにシリアル化されます。

メソッドをプロキシする必要があるのは面倒ですが、変装の祝福でもあります。新しいタイプであるため、選択したメソッドをfwし、新しいタイプを "typedef"に追加したいだけです。

お役に立てれば !

1
kofifus

私がC#で見つけたtypedefの最良の代替はusingです。たとえば、次のコードでコンパイラフラグを介して浮動小数点の精度を制御できます。

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

残念ながら、real_tを使用するすべてのファイルの先頭に配置する必要があります。現在、C#でグローバル名前空間型を宣言する方法はありません。

0
Aaron Franke