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;
}
しかし、そうすると、異なるタイプの関数に同じ定義を再利用する機能が失われます。
考え?
多くのクラスは一般的な制約として利用できません-Enumは別のものです。
デリゲートの場合、取得できる最も近いのは「:クラス」で、おそらくリフレクションを使用して(たとえば、静的コンストラクターで)T isデリゲートであることを確認します。
static GenericCollection()
{
if (!typeof(T).IsSubclassOf(typeof(Delegate)))
{
throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
}
}
編集:いくつかの回避策がこれらの記事で提案されています:
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、制約):
クラスタイプの制約は、次のルールを満たす必要があります。
そして、十分なVS2008はエラーを吐き出します:
error CS0702: Constraint cannot be special class 'System.Delegate'
この問題に関する情報と調査については、 こちら を参照してください。
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
{
}
}
デリゲートは既にチェーンをサポートしています。これはあなたのニーズを満たしていませんか?
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
}
}
内部でDelegate
を処理する必要がある状況に遭遇しましたが、一般的な制約が必要でした。具体的には、リフレクションを使用してイベントハンドラーを追加したかったのですが、デリゲートに汎用引数を使用したかったのです。 「ハンドラ」は型変数であり、コンパイラはHandler
をDelegate
にキャストしないため、以下のコードは機能しません。
public void AddHandler<Handler>(Control c, string eventName, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d);
}
ただし、変換を行う関数を渡すことができます。 convert
はHandler
引数を取り、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)
上記のように、デリゲートと列挙を一般的な制約として使用することはできません。 System.Object
およびSystem.ValueType
は、一般的な制約としても使用できません。
回避策は、ILで適切な呼び出しを作成する場合です。それは正常に動作します。
Jon Skeetによる良い例です。
http://code.google.com/p/unconstrained-melody/
Jon Skeetの本C#in Depth、第3版から参照を取りました。
コンパイラエラーCS0702
制約を特別なクラス「識別子」にすることはできません次のタイプは制約として使用できません。