C#のtypedefに相当


326

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を型定義する必要があります。

回答:


341

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

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

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

幸いにも、あなたが与える例がない暗黙のメソッドグループの変換-修正を持っています。イベントサブスクリプションラインを次のように変更できます。

gcInt.MyEvent += gcInt_MyEvent;

:)


11
これができることをいつも忘れます。多分、Visual Studioがより詳細なバージョンを提案するためです。しかし、ハンドラ名を入力する代わりに、Tab
キーを

11
私の経験では(これは不十分です)、たとえば、完全修飾タイプ名を指定する必要があります。 using MyClassDictionary = System.Collections.Generic.Dictionary<System.String, MyNamespace.MyClass>; それは正しいですか?そうでなければ、それはそのusing上の定義を考慮していないようです。
tunnuz 2010

3
typedef uint8 myuuid[16];「using」ディレクティブで変換できませんでした。using myuuid = Byte[16];コンパイルしません。タイプエイリアスのusing作成にのみ使用できます。宣言全体(配列サイズを含む)のエイリアスを作成できるので、はるかに柔軟性があるようです。この場合、代替手段はありますか?typedef
natenho 2014

2
@natenho:そうでもない。あなたが来ることができる最も近いものは、おそらく固定サイズのバッファを持つ構造体を持つことでしょう。
Jon Skeet、2014

1
@tunnuzネームスペース内で指定しない限り
John Smith

38

ジョンは本当に素晴らしい解決策を与えてくれました。

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

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

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


41
確かに良い方法ですが、それらの(迷惑な)封印型が存在し、そこでは機能しないことに注意してください。C#でtypedefが既に導入されていることを願っています。それは必死のニーズです(特にC ++プログラマーにとって)。
MasterMastic

1
私は、LikeTypeと呼ばれるこの状況のプロジェクトを作成しました。これは、基になるタイプから継承するのではなく、それをラップします。また、暗黙的に変換されますTOあなたのようなものを使用することができるように、基本となるタイプpublic class FooList : LikeType<IReadOnlyList<Foo>> { ... }して、どこでも、あなたが期待する、それを使用IReadOnlyList<Foo>。以下の私の答えは、より詳細を示しています。
Matt Klein

3
またFoo、たとえばを受け入れるテンプレートメソッドに渡された場合も、型を推測しませんList<T>。適切なtypedefがあれば可能です。
Aleksei Petrenko 2017

18

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

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

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


@palswimに感謝します。「typedef string Identifier」のようなものを探してここに来ました。だからあなたの提案はちょうど私が必要とするものかもしれません。
ヨーヨー2013年

6

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

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

イベントのサブスクライブに使用でき、明示的なキャストは不要

gcInt.MyEvent += LowestCommonHander;

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

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

私は真剣にLinqをよく見る必要があります...しかし、記録のために、私は当時2.0でビルドしていました(VS 2008でも)
Matthew Scharley

ああ、また、私はうまくサブスクライブできますが、イベントの引数を取得するには、安全のために、明示的なキャストと、できれば型チェックコードが必要です。
Matthew Scharley、2008年

9
構文は正しいですが、「Linq構文」だとは言いません。むしろそれはラムダ式です。ラムダはLinqを機能させるサポート機能ですが、完全に独立しています。基本的に、デリゲートを使用できる場所ならどこでも、ラムダ式を使用できます。
スコットドーマン

フェアポイント、ラムダを言ったはずです。デリゲートは.Net 2で機能しますが、ネストされたジェネリック型を再度明示的に宣言する必要があります。
キース

5

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

public delegate GenericHandler EventHandler<EventData>

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

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

gcInt.MyEvent += 

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


2
いや、それは私が例を生成するためにやったことです。しかし、事実がまだ混乱している可能性がある後、再びそれを見るために戻ってきます。
Matthew Scharley、2008年

あなたが言っていることがわかります。そのため、イベント署名を短くするか、FxCopの推奨事項から離れて、独自のデリゲート型の代わりにGeneric EventHandler <T>を使用します。しかし、その後、Jon Skeetによって提供された短縮版を使用してください:)
OregonGhost 08年

2
ReSharperを入手している場合は、長いバージョンがグレーになっているため、やり過ぎであることがわかります。また、「クイックフィックス」を使用して、それを再び取り除くことができます。
Roger Lipscombe

5

C ++とC#のどちらにも、既存の型と意味的に同一の新しい型を作成する簡単な方法がありません。私はそのような「typedefs」がタイプセーフなプログラミングに完全に不可欠であると思います、そしてそれは本当の恥C#にはそれらが組み込まれていません。void f(string connectionID, string username)to 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します。

お役に立てれば !


4

あなたが探している振る舞いを与える、私が作成した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));
    }
}

以下のような複雑な何かのためでしょうLikeType作業stackoverflow.com/questions/50404586/...?私はそれで遊んでみましたが、機能するクラスの設定を取得できません。
ジェイクロガン

それは実際にはLikeTypeライブラリの意図ではありません。LikeTypeの主な目的はPrimitive Obsessionを支援することです。そのため、ラッパータイプのようにラップされたタイプを渡すことができないようにしたいと考えています。のように、私が作成しAge : LikeType<int>、関数にが必要な場合はAge、呼び出し元がAgeではなくを渡していることを確認しintます。
マットクライン

そうは言っても、私はあなたの質問への回答があると思います。
マットクライン

3

これがそのコードです、お楽しみください!、私はdotNetReferenceから名前空間の行106内の "using"ステートメントを選択しました 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"};
        }
    }
}

2

封印されていないクラスの場合は、単にそれらから継承します。

public class Vector : List<int> { }

ただし、シールされたクラスの場合、そのような基本クラスを使用してtypedefの動作をシミュレートできます。

public abstract class Typedef<T, TDerived> where TDerived : Typedef<T, TDerived>, new()
{
    private T _value;

    public static implicit operator T(Typedef<T, TDerived> t)
    {
        return t == null ? default : t._value;
    }

    public static implicit operator Typedef<T, TDerived>(T t)
    {
        return t == null ? default : new TDerived { _value = t };
    }
}

// Usage examples

class CountryCode : Typedef<string, CountryCode> { }
class CurrencyCode : Typedef<string, CurrencyCode> { }
class Quantity : Typedef<int, Quantity> { }

void Main()
{
    var canadaCode = (CountryCode)"CA";
    var canadaCurrency = (CurrencyCode)"CAD";
    CountryCode cc = canadaCurrency;        // Compilation error
    Concole.WriteLine(canadaCode == "CA");  // true
    Concole.WriteLine(canadaCurrency);      // CAD

    var qty = (Quantity)123;
    Concole.WriteLine(qty);                 // 123
}

1

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.