web-dev-qa-db-ja.com

C#ジェネリックでは、デリゲート型の制約は許可されません

C#でクラスを定義することは可能ですか?

class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate

.NET 3.5でこの最後の夜を達成することはできませんでした。使ってみた

delegate, Delegate, Action<T> and Func<T, T>

これは何らかの方法で許容されるべきであると私には思えます。独自のEventQueueを実装しようとしています。

結局、これを行うことになりました[原始近似に気をつけてください]。

internal delegate void DWork();

class EventQueue {
    private Queue<DWork> eventq;
}

しかし、そうすると、異なるタイプの関数に同じ定義を再利用する機能が失われます。

考え?

76

多くのクラスは一般的な制約として利用できません-Enumは別のものです。

デリゲートの場合、取得できる最も近いのは「:クラス」で、おそらくリフレクションを使用して(たとえば、静的コンストラクターで)T isデリゲートであることを確認します。

static GenericCollection()
{
    if (!typeof(T).IsSubclassOf(typeof(Delegate)))
    {
        throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
    }
}
65
Marc Gravell

編集:いくつかの回避策がこれらの記事で提案されています:

http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html

http://jacobcarpenters.blogspot.com/2006_11_01_archive.html


C#2.0仕様 から読み取ることができます(20.7、制約):

クラスタイプの制約は、次のルールを満たす必要があります。

  • 型はクラス型でなければなりません。
  • タイプは封印してはいけません。
  • このタイプは、System.Array、System.Delegate、System.Enum、またはSystem.ValueTypeのいずれかのタイプであってはなりません。
  • タイプはオブジェクトであってはなりません。すべてのタイプはオブジェクトから派生するため、許可されている場合、このような制約は効果がありません。
  • 特定の型パラメーターの最大1つの制約をクラス型にすることができます。

そして、十分なVS2008はエラーを吐き出します:

error CS0702: Constraint cannot be special class 'System.Delegate'

この問題に関する情報と調査については、 こちら を参照してください。

13
Jorge Ferreira

IL Weaverでコンパイル時の依存関係を受け入れたい場合は、 Fody でこれを行うことができます。

Fodyへのこのアドインの使用 https://github.com/Fody/ExtraConstraints

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

public class Sample
{
    public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
    {        
    }
    public void MethodWithEnumConstraint<[EnumConstraint] T>()
    {
    }
} 

そしてこれにコンパイルされます

public class Sample
{
    public void MethodWithDelegateConstraint<T>() where T: Delegate
    {
    }

    public void MethodWithEnumConstraint<T>() where T: struct, Enum
    {
    }
}
10
Simon

はい、C#7.3では可能です。ConstraintsファミリはEnumDelegateおよびunmanaged型を含むように増加しました。問題なくこのコードを書くことができます:

void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged
    {

    }

便利なリンク:

C#の未来 、Microsoft Build 2018から

C#7.3の新機能

9
mshwf

デリゲートは既にチェーンをサポートしています。これはあなたのニーズを満たしていませんか?

public class EventQueueTests
{
    public void Test1()
    {
        Action myAction = () => Console.WriteLine("foo");
        myAction += () => Console.WriteLine("bar");

        myAction();
        //foo
        //bar
    }

    public void Test2()
    {
        Action<int> myAction = x => Console.WriteLine("foo {0}", x);
        myAction += x => Console.WriteLine("bar {0}", x);
        myAction(3);
        //foo 3
        //bar 3
    }

    public void Test3()
    {
        Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; };
        myFunc += x => { Console.WriteLine("bar {0}", x); return x + 1; };
        int y = myFunc(3);
        Console.WriteLine(y);

        //foo 3
        //bar 3
        //4
    }

    public void Test4()
    {
        Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; };
        Func<int, int> myNextFunc = x => { x = myFunc(x);  Console.WriteLine("bar {0}", x); return x + 1; };
        int y = myNextFunc(3);
        Console.WriteLine(y);

        //foo 3
        //bar 5
        //6
    }

}
3
Amy B

内部でDelegateを処理する必要がある状況に遭遇しましたが、一般的な制約が必要でした。具体的には、リフレクションを使用してイベントハンドラーを追加したかったのですが、デリゲートに汎用引数を使用したかったのです。 「ハンドラ」は型変数であり、コンパイラはHandlerDelegateにキャストしないため、以下のコードは機能しません。

public void AddHandler<Handler>(Control c, string eventName, Handler d) {
  c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d);
}

ただし、変換を行う関数を渡すことができます。 convertHandler引数を取り、Delegateを返します。

public void AddHandler<Handler>(Control c, string eventName, 
                  Func<Delegate, Handler> convert, Handler d) {
      c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d));
}

コンパイラーは満足しています。メソッドの呼び出しは簡単です。たとえば、WindowsフォームコントロールのKeyPressイベントにアタッチする場合:

AddHandler<KeyEventHandler>(someControl, 
           "KeyPress", 
           (h) => (KeyEventHandler) h,
           SomeControl_KeyPress);

どこ SomeControl_KeyPressはイベントターゲットです。キーはコンバーターラムダです-動作しませんが、有効なデリゲートを与えたコンパイラーを納得させます。

(280Z28から)@Justin:なぜこれを使用しないのですか?

public void AddHandler<Handler>(Control c, string eventName, Handler d) { 
  c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate); 
} 

(終了280Z28)

3
Justin Bailey

上記のように、デリゲートと列挙を一般的な制約として使用することはできません。 System.ObjectおよびSystem.ValueTypeは、一般的な制約としても使用できません。

回避策は、ILで適切な呼び出しを作成する場合です。それは正常に動作します。

Jon Skeetによる良い例です。

http://code.google.com/p/unconstrained-melody/

Jon Skeetの本C#in Depth、第3版から参照を取りました。

2
maxspan

[〜#〜] msdn [〜#〜]

コンパイラエラーCS0702

制約を特別なクラス「識別子」にすることはできません次のタイプは制約として使用できません。

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType。
1
Rahul Nikate