1

オブジェクトが独自のイベントを発生させるために使用できる拡張メソッドを試しています。

ほぼ希望どおりに機能していますが、渡された引数をActivatorに頼らずにEventArgsのコンストラクター引数に変換できるように改善できるかどうかを知りたいと思います。

これが可能かどうかは疑わしいと前もって言いますが、他の人が持っているコーディングのトリックに本当に驚かされることがあるので、試してみるつもりです...

void Main()
{
    var c = new C();
    c.E += (s, e) => Console.WriteLine (e.Message);
    c.Go();
}

public class C
{
    public event EventHandler<Args> E;
    public void Go()
    {
        Console.WriteLine ("Calling event E...");

        // This version doesn't know the type of EventArgs so it has to use Activator
        this.Fire(E, "hello");

        // This version doesn't know ahead of time if there are any subscribers so it has to use a delegate
        this.Fire(E, () => new Args("world"));

        // Is there some way to get the best of both where it knows the type but can delay the 
        // creation of the event args?
        //this.Fire<Args>("hello");
    }
}

public class Args : EventArgs
{
    public Args(string s)
    {
        Message = s;
    }
    public string Message { get; set; }
}

public static class Ext
{
    public static void Fire<T>(this object source, EventHandler<T> eventHander, Func<T> eventArgs) where T : EventArgs
    {
        if (eventHander != null)
            eventHander(source, eventArgs());
    }

    public static void Fire<T>(this object source, EventHandler<T> eventHander, params object[] args) where T : EventArgs
    {
        if (eventHander != null)
            eventHander(source, (T)Activator.CreateInstance(typeof(T), args));
    }
}
4

1 に答える 1

2

私は以前にこのようなことをしましたが、代わりに新しいEventArgs/EventHandlerラッパーを使用する方法を取りました。暗黙的な変換とジェネリックスを使用して、イベント引数との間の変換を自動的に処理します。

public delegate void DataEventHandler<TSender, TEventArgs>(TSender sender, DataEventArgs<TEventArgs> eventArgs);
public delegate void DataEventHandler<TEventArgs>(DataEventArgs<TEventArgs> eventArgs);

public class DataEventArgs<TEventArgs>
{
    public TEventArgs Args { get; private set; }

    public DataEventArgs(TEventArgs args)
    {
        this.Args = args;
    }

    public static implicit operator TEventArgs(DataEventArgs<TEventArgs> args)
    {
        return args.Args;
    }

    public static implicit operator DataEventArgs<TEventArgs>(TEventArgs args)
    {
        return new DataEventArgs<TEventArgs>(args);
    }
}

送信者の有無にかかわらずオーバーロードを設定します。おそらく良い考えではありませんが、少なくともそれで遊ぶことができます。

object次に、拡張メソッドは、ちょっとひどいタイプに配置する代わりに、すべてのオブジェクト(私は思う)が、実際には適用できない場合でも、インテリセンスで表示/利用可能にするので、DataEventHandlers自体に関連付けます:

public static class MyExtensions
{
    public static void Fire<TSender, TEventArgs>(this DataEventHandler<TSender, TEventArgs> eventHandler, TSender sender, TEventArgs args)
    {
        if (eventHandler!= null)
            eventHandler(sender, args);
    }

    public static void Fire<TEventArgs>(this DataEventHandler<TEventArgs> eventHandler, TEventArgs args)
    {
        if (eventHandler != null)
            eventHandler(args);
    }
}

(私はそれをと同じ名前空間に置いているDataEventHandlerので、名前空間を持つイベントをusingステートメントとして使用すると仮定すると、自動的に利用可能/インポートされることに注意してください)

拡張メソッドはすでに引数の型を知っていますが、まだargsオブジェクトとして渡されていません。むしろ、元のタイプとして渡され、イベントに登録者がいる場合は、最後の呼び出しでのみeventHandler(sender, args)暗黙的にイベント引数に変換されます。

クラスCは次のようになります。

public class C
{
    public event DataEventHandler<string> E;
    public event DataEventHandler<C, string> EWithSender;

    public void Go()
    {
        Console.WriteLine ("Calling event E...");

        E.Fire("hello");
        EWithSender.Fire(this, "hello");
    }
}

のイベント宣言は、;Cを使用して明示的にマークを付けないことに注意してください。DataEventHandler<DataEventArgs<string>>これは、デリゲートパラメータによって暗黙的に処理されます。

呼び出しコードは次のようになります。

C c = new C();
c.E += (args) => PrintOut(args);
c.EWithSender += (sender, args) => Console.WriteLine("Sender Type: " + sender.GetType().Name + " -> Args: " + args.Args);
c.Go();


private void PrintOut(string text)
{
    Console.WriteLine(text);
}

繰り返しになりますが、イベント引数は、メソッドに渡されるときに、ラップされたデータ型に暗黙的に変換して戻すことができます(ただし、そうする必要はありません)。

さて、これにはいくつかの欠点があります。主に、私の意見では、タイピングに関する標準の.NET EventHandlerの慣習に違反している、独自のイベント引数を作成するのが難しいなどです。特に、独自のEventArgsサブクラスを作成せず、代わりにデータオブジェクトを渡すだけなので(基本的な値型、または私自身のカスタムクラスまたはデータモデルのいずれか)。それは私にかなり役立ったが、実際には私はそれがますます役に立たなくなっていることに気付いている。私はこのスタイル/実装を支持していませんが、おそらくそれはあなたにいくつかのアイデアを与えるでしょう。

于 2012-09-29T02:06:25.757 に答える