読者です 読者をやめる 読者になる 読者になる
ようこそ。睡眠不足なプログラマのチラ裏です。

拡張メソッドバブルのこのご時世、ふつうのデリゲートをジェネリックデリゲートに変換したいことってあるよね。

プログラミング C#3.0 C#4.0 イディオム

拡張メソッドバブルのこのご時世、ジェネリックデリゲートに対して拡張メソッドが定義されていることとかも少なくない。
ということで、ふつうのデリゲートをジェネリックデリゲートとして扱いたい!なんてケースもあるようなないような。いや、きっとあります。
というわけで、いつでもデリゲートを別のデリゲート変換できるようにしておくと何かと便利っぽい感じがします。
 
コードは以下の通り。

using System;
using System.Linq;
using System.ComponentModel;

namespace ConsoleApplication1
{
    class Program
    {
        static event CancelEventHandler TestHandler = (sender, e) => { e.Cancel = true; Console.WriteLine(sender); };
        static void Main(string[] args)
        {
            TestHandler += (sender, e) => { e.Cancel = true; Console.Write(sender); Console.WriteLine(sender); };

            var d1 = TestHandler.Convert<EventHandler<CancelEventArgs>>();
            var d2 = TestHandler.Convert<Action<object, CancelEventArgs>>();

            Console.WriteLine(d1.GetType());
            d1.Invoke("hoge", new CancelEventArgs());
            Console.WriteLine(d2.GetType());
            d2.Invoke("fuga", new CancelEventArgs());

            try
            {
                var d3 = TestHandler.Convert<Action<object, EventArgs>>();
            }
            catch
            {
                Console.WriteLine("orz");
            }

            Action<object, EventArgs> result = null;
            var isConv = TestHandler.TryConvert<Action<object, EventArgs>>(out result);
            Console.WriteLine(result);
            Console.WriteLine(isConv);

            Console.ReadLine();
        }
    }

    public static class DelegateExtentions
    {
        public static T Convert<T>(this Delegate self) where T : class
        {
            return self.Convert(typeof(T)) as T;
        }

        private static Delegate Convert(this Delegate self, Type type)
        {
            if (self == null) return null;
            var delegates = self.GetInvocationList().ToList();
            var convResult = delegates.ConvertAll(d => Delegate.CreateDelegate(type, d.Target, d.Method, true));
            return Delegate.Combine(convResult.ToArray());
        }

        public static bool TryConvert<T>(this Delegate self, out T result) where T : class
        {
            result = default(T);
            var r = result as Delegate;
            if (r == null) return false;
            return self.TryConvert(typeof(T), out r);
        }

        private static bool TryConvert(this Delegate self, Type type, out Delegate result) 
        {
            result = null;
            if (self == null) return false;
            var delegates = self.GetInvocationList().ToList();
            var convResult = delegates.ConvertAll(d => Delegate.CreateDelegate(type, d.Target, d.Method, false));
            if (convResult.Exists(x => x == null)) return false;
            result = Delegate.Combine(convResult.ToArray());
            return true;
        }
    }
}


実行結果

System.EventHandler`1[System.ComponentModel.CancelEventArgs]
hoge
hogehoge
System.Action`2[System.Object,System.ComponentModel.CancelEventArgs]
fuga
fugafuga
orz

False


いわゆるTryParseパターン的な意味でTryConvertってみましたが、
例外を発生させないConvertメソッドだけ作って、nullが返ってきたら変換失敗って仕様の方がシンプルかも?なーんて思ったりも。