リフレクション (情報工学)
例 > C#

次の例はC#による例で、より進んだリフレクションの用法を示している。プログラムはコマンドラインからアセンブリ名を入力としてとる。アセンブリとはクラスライブラリのようなものである。アセンブリが読み込まれると、アセンブリ内で定義されたメソッドを検索するためにリフレクションが用いられる。見つかった各メソッドに対し、最近変更があったかどうかをリフレクションを使って調べている。もし変更があり、かつ引数をとらないメソッドであれば、メソッド名と戻り値の型を表示する。
最近変更されたかどうかを調べるために、開発者はカスタム属性を使う必要がある。
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Recent;

namespace Reflect
{
    class Program
    {
        private Assembly a;
        Program(String assemblyName)
        {
            a = Assembly.Load(new AssemblyName(assemblyName));
            // 指定されたアセンブリにSupportsRecentlyModified属性が適用されていることを確認。
            Attribute c = Attribute.GetCustomAttribute(a, typeof(SupportsRecentlyModifiedAttribute));
            if (c == null)
            {
                 // "SupportsRecentlyModified"属性の取得に失敗。
                 // つまり、アセンブリは"RecentlyModified"属性をサポートしていない。
                 throw new Exception("アセンブリ" + assemblyName + " は必要な属性をサポートしていません。");
            }
            Console.WriteLine("読み込み完了: " + a.FullName);
        }

        public void FindNewMethodsWithNoArgs()
        {
            // アセンブリに定義されている型をすべて取得する
            Type[] t = a.GetTypes();
            foreach (Type type in t)
            {
                // この型がクラスでなければスキップ。
                if (!type.IsClass)
                    continue;
                Console.WriteLine("クラス名: " + type.FullName);
                MethodInfo[] methods = type.GetMethods();
                foreach (MethodInfo method in methods)
                {
                    object[] ab = method.GetCustomAttributes(typeof(RecentlyModifiedAttribute), true);
                    // 属性がひとつも取得できなければ、このメソッドは古いということである。
                    if (ab.Length != 0)
                    {
                        // そうでなければただ1つの属性のみ取得される。
                        // なぜなら、"RecentlyModified"属性は他の属性との併用ができないからである。

                        Console.Write("\t更新されたメソッド: " + method.Name);
                        if (method.GetParameters().Length > 0)
                            break;

                        // 開発者が指定したコメントを取得するために、
                        // "RecentlyModifiedAttribute"属性のインスタンスを用いる。
                        Console.WriteLine("\t" + (ab[0] as RecentlyModifiedAttribute).comment);
                        Console.WriteLine("\t\t戻り値: " + method.ReturnType.Name);
                    }
                }
            }
        }

        static void Main(string[] args)
        {
            try
            {
                Program reflector = new Program("UseAttributes");
                reflector.FindNewMethodsWithNoArgs();
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e.Message);
            }
        }
    }
}
上で使用したカスタム属性の実装を次に示す。
using System;
using System.Collections.Generic;
using System.Text;

namespace Recent
{
    // この属性はメソッドにしか適用できず、また他の属性との併用もできない。
    [AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)]
    public class RecentlyModifiedAttribute : Attribute
    {
        // この属性はメソッドに対して適用される。
        // 引数なしで([RecentlyModified])、
        // またはコメントとともに([RecentlyModified(comment="<someComment>")])適用できる。

        private String Comment = "このメソッドは最近変更されました。";
        
        public RecentlyModifiedAttribute()
        {
            // 属性のインスタンス化のための空コンストラクタ。
            // 必須な引数はないのでコンストラクタは空。
        }

        // 省略可能な引数"comment"の定義。属性が使用される際に名前つき引数として指定される。
        public String comment
        {
            get
            {
                return Comment;
            }
            set
            {
                Comment = comment;
            }
        }
    }

    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=false)]
    public class SupportsRecentlyModifiedAttribute : Attribute
    {
        // この属性は引数なしでアセンブリに適用される。
        // as in [SupportsRecentlyModified]
        public SupportsRecentlyModifiedAttribute()
        {
            // 必須な引数はないのでコンストラクタは空。
        }
    }
}
また、このカスタム属性を使用したクラスの定義例を次に示す。
using System;
using System.Collections.Generic;
using System.Text;
using Recent;

// アセンブリが "RecentlyModified"属性をサポートすることを示すために
// "SupportsRecentlyModified"属性を適用する。
[assembly: SupportsRecentlyModified]

namespace Reflect
{
    class UseAttributes
    {
        private Object info;

        public UseAttributes()
        {
            info = (object) "Hello World";
        }

        public void OldMethodWithNoArgs()
        {
            Console.WriteLine("これは引数をとらない古いメソッドである。");
        }

        // メソッドが最近変更されたということを示すために"RecentlyModified"属性を適用する。
        [RecentlyModified]
        public void NewMethodWithNoArgs()
        {
            Console.WriteLine("これは引数をとらない新しいメソッドである。");
        }

        public void OldMethodWithOneArg(object something)
        {
            info = something;
            Console.WriteLine("これは引数を1つとる古いメソッドである。");
        }

        [RecentlyModified]
        public void NewMethodWithOneArg(object something)
        {
            info = something;
            Console.WriteLine("これは引数を1つとる新しいメソッドである。");
        } 
    }
}