雑記 |
Visual Basic 中学校 > 雑記 >
2020/7/5
この記事が対象とする製品・バージョン
![]() |
Visual Studio 2019 | ◎ | 対象です。 |
![]() |
Visual Studio 2017 | ◎ | 対象です。 |
![]() |
Visual Studio 2015 | ◎ | 対象です。 |
![]() |
Visual Studio 2013 | ◎ | 対象ですが、サンプルの一部は動作しません。 |
![]() |
Visual Studio 2012 | ◎ | 対象ですが、サンプルの一部は動作しません。 |
![]() |
Visual Studio 2010 | ◎ | 対象ですが、サンプルの一部は動作しません。 |
![]() |
Visual Studio 2008 | ◎ | 対象ですが、サンプルの一部は動作しません。 |
![]() |
Visual Studio 2005 | ◎ | 対象ですが、サンプルの一部は動作しません。 |
![]() |
Visual Studio 2003 | ◎ | 対象ですが、サンプルの一部は動作しません。 |
![]() |
Visual Studio (2002) | ◎ | 対象ですが、サンプルの一部は動作しません。 |
この記事ではクラスや型のメンバーをリフレクション機能で取得する場合のBindingFlagsの組み合わせをまとめます。
参考:目的によってはBindingFlagsを使用せずに用意されている機能だけ済む場合もあります。
BindingsFlagにはリフレクションでメンバーを検索するときの対象範囲を指定する意味があります。対象範囲は「アクセススコープ」と「静的メンバー or 非静的メンバー」の2つの軸で示します。
※静的メンバーのことを、クラスメンバー・共有メンバーと呼ぶ場合もあります。非静的メンバーのことを、インスタンスメンバー・非共有メンバーと呼ぶ場合もあります。
両方の軸から1つずつ選択して組み合わせる必要があります。
フラグを使ってリフレクションでさまざまな機能を呼び出すプログラム例はこの記事の後半にあります。
検索対象 | 指定すべきフラグ | |
---|---|---|
Public | Public 以外 (※1) |
|
○ | BindingFlags.Public | |
○ | BindingFlags.NonPublic | |
○ | ○ | BindingFlags.Public BindingFlags.NonPublic |
※1 基底クラスのPrivateメンバーは対象にできません。
検索対象 | 指定すべきフラグ | |||
---|---|---|---|---|
対象クラスの 非静的メンバー |
対象クラスの 静的メンバー |
基底クラスの 非静的メンバー |
基底クラスの 静的メンバー |
|
○ | BindingFlags.Instance BindingFlags.DeclaredOnly |
|||
○ | ○ | BindingFlags.Instance | ||
○ | BindingFlags.Static | |||
○ | ○ | BindingFlags.Static BindingFlags.FlattenHierarchy |
||
○ | ○ | BindingFlags.Instance BindingFlags.Static BindingFlags.DeclaredOnly |
||
○ | ○ | ○ | BindingFlags.Instance BindingFlags.Static |
|
○ | ○ | ○ | BindingFlags.Instance BindingFlags.Static BindingFlags.DeclaredOnly BindingFlags.FlattenHierarchy |
|
○ | ○ | ○ | ○ | BindingFlags.Instance BindingFlags.Static BindingFlags.FlattenHierarchy |
VBの場合 Or でフラグを組み合わせます。C#の場合 | でフラグを組み合わせます。
基底クラスを含む場合、直接の基底クラスだけでなく、すべての継承の階層の基底クラスが対象になります。
フラグを省略した場合、 BindingFlags.Public ・ BindingFlags.Instance ・ BindingFlags.Staticの組み合わせが指定されたものとみなされます。
VB
Dim flag As BindingFlags = BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.Static Or BindingFlags.FlattenHierarchy |
C#
BindingFlags flag = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy |
※基底クラスのPrivateメンバーを取得できる組み合わせはありません。
VB
Dim flag As BindingFlags = BindingFlags.Public Or BindingFlags.Instance Or BindingFlags.DeclaredOnly |
C#
BindingFlags flag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly; |
VB
Dim flag As BindingFlags = BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Static Or BindingFlags.FlattenHierarchy |
C#
BindingFlags flag = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy; |
それぞれのフラグ単体のおおよその意味は次の通りです。
フラグ | 意味 |
---|---|
BindingFlags.Public | Public なメンバーを対象とします。 |
BindingFlags.NonPublic | Public ではないメンバーを対象とします。 |
BindingFlags.Instance | 非静的メンバー(インスタンスメンバー)を対象とします。 |
BindingFlags.Static | 静的メンバー(VBでは共有メンバー)を対象とします。たとえばVBの場合、Sharedで、C#の場合staticで宣言されているメンバーです。 |
BindingFlags.DeclaredOnly | 基底クラスで定義されているメンバーを対象外にします。 |
BindingFlags.FlattenHierarchy | 基底クラスで定義されている静的メンバーを対象にします。 |
参考
https://docs.microsoft.com/ja-jp/dotnet/api/system.reflection.bindingflags
BindingFlagsではメソッド・プロパティなどの区別はできません。区別する方法は2つあります。
取得したいもの | 名前を指定して1つ取得したい場合に使用するメソッド | 複数取得したい場合に使用するメソッド | 備考 |
---|---|---|---|
すべてのメンバー | GetMember | GetMembers | |
メソッド | GetMethod | GetMethods | 普段隠蔽されている自動生成されたメソッドも含みます。 |
プロパティ | GetProperty | GetProperties | |
イベント | GetEvent | GetEvents | |
フィールド | GetField | GetFields | クラス/構造体レベルの定数や変数です。 |
コンストラクター | GetConstructor | GetConstructors | VBの場合、New を指します。 |
内部クラス/構造体 | GetNestedType | GetNestedTypes |
Typeクラスのインスタンスを取得するには、GetType メソッドを使用します。
型からTypeクラスのインスタンスを取得するには、VBの場合GetType(xxx) 、C#の場合typeof(xxx)を使用します。
インスタンスからTypeを取得する例
VB
Dim type
As Type = obj.GetType Dim prop As PropertyInfo = type.GetProperty("Enabled") |
C#
Type type = obj.GetType(); PropertyInfo prop = type.GetProperty("Enabled"); |
型(System.IO.DriveInfo)からTypeを取得する例
VB
Dim type
As Type =
GetType(System.IO.DriveInfo) Dim prop As PropertyInfo = type.GetProperty("TotalSize") |
C#
Type type =
typeof(System.IO.DriveInfo); PropertyInfo prop = type.GetProperty("TotalSize"); |
GetMemberメソッドまたはGetMembersメソッドで取得できるMemberInfoクラスのMemberTypeプロパティを使ってこれらの種類を識別することもできます。
参考
https://docs.microsoft.com/ja-jp/dotnet/api/system.reflection.membertypes
BindingFlagsではPublicかPublic以外かの区別ができるだけで、それ以上詳細なアクセススコープの区別はできません。
取得したMethodInfoやFieldInfoのプロパティを使って詳細なスコープを識別できます。
プロパティ | VBで表現した スコープ |
C#で表現した スコープ |
---|---|---|
IsPublic | Public | public |
IsAssembly | Friend | internal |
IsFamilyOrAssembly | Protected Friend | protected internal |
IsFamily | Protected | protected |
IsFamilyAndAssembly | Private Protected | private protected |
IsPrivate | Private | private |
PropertyInfoやEventInfoはこれらのプロパティを持っていません。プロパティの場合、GetとSetで異なるスコープにできることもあり、内部生成されたメソッドの方にこれらのプロパティがあります。
PropertyInfoからGetとSetのメソッドを取得するにはGetAccessorsメソッドを使用します。
プロパティやイベントを定義すると内部でメソッドが自動生成されます。これらのメソッドは普段隠蔽されていますがリフレクションを使うと存在が露呈します。
XXXプロパティから自動生成されるメソッドは get_XXX と set_XXX です。(GetまたはSetが定義されていない場合は対応するメソッドは生成されません。)
XXXイベントから自動生成されるメソッドは add_XXX と remove_XXX です。
これらの自動生成されたメソッドは IsSpecialNameプロパティが True になっています。
上記の他にオーバーロードされた演算子もIsSpecialNameがTrueです。
これを利用して、プログラマーによって明示的に定義されたメソッドのみの一覧から作成するには次のようにします。
この例ではStopWatchクラスのメソッドの名前の一覧を出力ウィンドウのデバッグに表示します。→ 表示される場所
VB
Dim flag
As BindingFlags =
BindingFlags.Public
Or BindingFlags.NonPublic
Or BindingFlags.Instance
Or BindingFlags.Static
Or BindingFlags.FlattenHierarchy Dim members As List(Of MemberInfo) = GetType(System.Diagnostics.Stopwatch).GetMembers(flag).ToList For Each method As MethodInfo In members.OfType(Of MethodInfo).ToArray If method.IsSpecialName Then members.Remove(method) End If Next For Each member As MethodInfo In members.OfType(Of MethodInfo) Debug.WriteLine(member.Name) Next |
C#
BindingFlags flag =
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.FlattenHierarchy; List<MemberInfo> members = typeof(System.Diagnostics.Stopwatch).GetMembers(flag).ToList(); foreach (MethodInfo method in members.OfType<MethodInfo>().ToArray()) { if (method.IsSpecialName) { members.Remove(method); } } foreach (MethodInfo member in members.OfType<MethodInfo>()) { System.Diagnostics.Debug.WriteLine(member.Name); } |
変数 obj で表されるオブジェクトの Public で定義されている Focus メソッドを取得して呼び出します。
VB
Dim flag
As BindingFlags =
BindingFlags.Public
Or BindingFlags.Instance Dim method As MethodInfo = obj.GetType.GetMethod("Focus", flag) If method Is Nothing Then Throw New ArgumentException("Focusメソッドは存在しません。") End If method.Invoke(obj, Nothing) |
C#
BindingFlags flag =
BindingFlags.Public |
BindingFlags.Instance; MethodInfo method = obj.GetType().GetMethod("Focus", flag); if (method == null) { throw new ArgumentException("Focusメソッドは存在しません。"); } method.Invoke(obj, null); |
System.Diagnostics.Stopwatch型のメンバーの一覧を出力ウィンドウのデバッグに表示します。 → 表示される場所
例としてメンバーの名前やプロパティ・メソッドの区別・型など一部の情報を出力します。他にもこの例で扱っていない様々な情報が出力できます。
この例ではプロパティの情報はGetの方の情報を出力しています。GetとSetで違うスコープが設定されている場合などには対応していません。Getがないプロパティがあるとエラーになります。
VB
Dim flag
As BindingFlags =
BindingFlags.Public
Or BindingFlags.NonPublic
Or BindingFlags.Instance
Or BindingFlags.Static
Or BindingFlags.FlattenHierarchy Dim members As MemberInfo() = GetType(System.Diagnostics.Stopwatch).GetMembers(flag) For Each member As MemberInfo In members Debug.Write(member.Name & vbTab) 'メンバーの名前 Debug.Write(member.MemberType.ToString & vbTab) 'メソッドやプロパティなどの区別 Dim method As MethodInfo = Nothing If member.MemberType = MemberTypes.Property Then 'メンバーがプロパティの場合Get操作を行う自動生成されたメソッドを取得します。 Dim prop As PropertyInfo = DirectCast(member, PropertyInfo) method = prop.GetAccessors(True).First End If If member.MemberType = MemberTypes.Method Then 'メンバーがメソッドの場合 MethodInfo型に変換します。 method = DirectCast(member, MethodInfo) End If If method IsNot Nothing Then Select Case True Case method.IsPublic Debug.Write("Public" & vbTab) Case method.IsAssembly Debug.Write("Friend" & vbTab) Case method.IsFamilyOrAssembly Debug.Write("Protected Friend" & vbTab) Case method.IsFamily Debug.Write("Protected" & vbTab) Case method.IsFamilyAndAssembly Debug.Write("Private Protected" & vbTab) Case method.IsPrivate Debug.Write("Private" & vbTab) End Select Debug.Write(method.ReturnType.Name & vbTab) 'メソッドの戻り値 End If Debug.Write("(" & member.DeclaringType.Name & "で定義)") 'このメンバーが定義されているクラス Debug.WriteLine("") Next |
C#
BindingFlags flag =
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.FlattenHierarchy; MemberInfo[] members = typeof(System.Diagnostics.Stopwatch).GetMembers(flag); foreach (MemberInfo member in members) { System.Diagnostics.Debug.Write(member.Name + "\t"); //メンバーの名前 System.Diagnostics.Debug.Write(member.MemberType.ToString() + "\t"); //メソッドやプロパティなどの区別 MethodInfo method = null; if (member.MemberType == MemberTypes.Property) { //メンバーがプロパティの場合Get操作を行う自動生成されたメソッドを取得します。 PropertyInfo prop = member as PropertyInfo; method = prop.GetAccessors(true).First(); } if (member.MemberType == MemberTypes.Method) { //メンバーがメソッドの場合 MethodInfo型に変換します。 method = member as MethodInfo; } if (method != null) { if (method.IsPublic) { System.Diagnostics.Debug.Write("public" + "\t"); } else if (method.IsAssembly) { System.Diagnostics.Debug.Write("internal" + "\t"); } else if (method.IsFamilyOrAssembly) { System.Diagnostics.Debug.Write("protected internal" + "\t"); } else if (method.IsFamily) { System.Diagnostics.Debug.Write("protected" + "\t"); } else if (method.IsFamilyAndAssembly) { System.Diagnostics.Debug.Write("private protected" + "\t"); } else if (method.IsPrivate) { System.Diagnostics.Debug.Write("private" + "\t"); } System.Diagnostics.Debug.Write(method.ReturnType.Name + "\t"); //メソッドの戻り値 } System.Diagnostics.Debug.Write("(" + member.DeclaringType.Name + "で定義)"); //このメンバーが定義されているクラス System.Diagnostics.Debug.WriteLine(""); } |
この例ではSystem.IO.DriveInfoクラスの型情報からコンストラクラーを取得して、DriveInfoクラスのインスタンスを生成します。次にAvailableFreeSpaceプロパティを呼び出してCドライブの空き容量を取得します。→ 表示される場所
この例はあくまでリフレクションの参考にすることが目的であり、同じ機能を実現するだけならばリフレクションを使わないでDriveInfoクラスを操作するほうがはるかに簡単で優れています。
VB
Dim flag
As BindingFlags =
BindingFlags.Public
Or BindingFlags.NonPublic
Or BindingFlags.Instance Dim ctors As ConstructorInfo() = GetType(System.IO.DriveInfo).GetConstructors(flag) 'Dim newObject As New System.IO.DriveInfo("C") とほぼ等価 Dim newObject As Object = ctors.First.Invoke({"C"}) Dim prop As PropertyInfo = newObject.GetType.GetProperty("AvailableFreeSpace") 'Dim freeSpace As Long = newObject.AvailableFreeSpace とほぼ等価 Dim freeSpace = prop.GetMethod.Invoke(newObject, Nothing) Dim freeSpaceGB As Long = CLng(freeSpace) \ 1024 \ 1024 \ 1024 Debug.WriteLine("Cドライブの空き容量:" & freeSpaceGB.ToString & " GB") |
C#
BindingFlags flag =
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance; ConstructorInfo[] ctors = typeof(System.IO.DriveInfo).GetConstructors(flag); //var newObject = new System.IO.DriveInfo("C"); とほぼ等価 Object newObject = ctors.First().Invoke(new object[] { "C" }); PropertyInfo prop = newObject.GetType().GetProperty("AvailableFreeSpace"); //long freeSpace = newObject.AvailableFreeSpace; とほぼ等価 var freeSpace = prop.GetMethod.Invoke(newObject, null); long freeSpaceGB = (long)freeSpace / 1024 / 1024 / 1024; System.Diagnostics.Debug.WriteLine("Cドライブの空き容量:" + freeSpaceGB.ToString() + " GB"); |
以前私は自作のライブラリが新しい環境に対応できないという問題に直面し、ここで紹介するこの方法でPrivateなフィールドに値を設定することで回避できたことがあります。
データモデルのようなクラスの場合はリフレクションで値を読み書きする機能はしばしばありますが、自分でこの方法を使用するのはあまり良い手ではなく、何かの逃げの場合が多いように思います。
Dim flag
As BindingFlags =
BindingFlags.NonPublic
Or BindingFlags.Instance Dim field As FieldInfo = obj.GetType.GetField("IsInitialized", flag) 'IsInitializedフィールドに True を設定します。 field.SetValue(obj, True) Debug.WriteLine(obj.ToString) |
C#
BindingFlags flBindingFlags flag =
BindingFlags.NonPublic |
BindingFlags.Instance; FieldInfo field = obj.GetType().GetField("IsInitialized", flag); //IsInitializedフィールドに True を設定します。 field.SetValue(obj, true); System.Diagnostics.Debug.WriteLine(obj.ToString()); |