雑記
 

BindingFlagsの効果

2020/7/5

この記事が対象とする製品・バージョン

VS2019 Visual Studio 2019 対象です。
VS2017 Visual Studio 2017 対象です。
VS2015 Visual Studio 2015 対象です。
VS2013 Visual Studio 2013 対象ですが、サンプルの一部は動作しません。
VS2012 Visual Studio 2012 対象ですが、サンプルの一部は動作しません。
VS2010 Visual Studio 2010 対象ですが、サンプルの一部は動作しません。
VS2008 Visual Studio 2008 対象ですが、サンプルの一部は動作しません。
VS2005 Visual Studio 2005 対象ですが、サンプルの一部は動作しません。
VS.NET 2003 Visual Studio 2003 対象ですが、サンプルの一部は動作しません。
VS.NET 2002 Visual Studio (2002) 対象ですが、サンプルの一部は動作しません。

目次

この記事ではクラスや型のメンバーをリフレクション機能で取得する場合のBindingFlagsの組み合わせをまとめます。

参考:目的によってはBindingFlagsを使用せずに用意されている機能だけ済む場合もあります。

https://docs.microsoft.com/ja-jp/dotnet/api/system.reflection.runtimereflectionextensions?view=netcore-3.1#methods

1.フラグの組み合わせ方

1-1.組み合わせ方

BindingsFlagにはリフレクションでメンバーを検索するときの対象範囲を指定する意味があります。対象範囲は「アクセススコープ」と「静的メンバー or 非静的メンバー」の2つの軸で示します。

※静的メンバーのことを、クラスメンバー・共有メンバーと呼ぶ場合もあります。非静的メンバーのことを、インスタンスメンバー・非共有メンバーと呼ぶ場合もあります。

両方の軸から1つずつ選択して組み合わせる必要があります

フラグを使ってリフレクションでさまざまな機能を呼び出すプログラム例はこの記事の後半にあります。

アクセススコープ の軸

検索対象 指定すべきフラグ
Public Public 以外
(※1)
  BindingFlags.Public
  BindingFlags.NonPublic
BindingFlags.Public
BindingFlags.NonPublic

※1 基底クラスのPrivateメンバーは対象にできません。

 

静的メンバー or 非静的メンバー の軸

検索対象 指定すべきフラグ
対象クラスの
非静的メンバー
対象クラスの
静的メンバー
基底クラスの
非静的メンバー
基底クラスの
静的メンバー
      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の組み合わせが指定されたものとみなされます。

 

例1.(基底クラスのPrivateメンバーを除く)すべてのメンバーを対象にするフラグの組み合わせ

VB

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応


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メンバーを取得できる組み合わせはありません。

 

例2:Publicなメンバー で かつ 対象クラスの 非静的メンバー で かつ 基底クラスのメンバーは検索対象に含めない場合 のフラグの組み合わせ

VB

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応


Dim flag As BindingFlags = BindingFlags.Public Or BindingFlags.Instance Or BindingFlags.DeclaredOnly

C#


BindingFlags
flag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;

 

例3:すべてのアクセススコープで対象クラスと基底クラスの静的メンバーを対象にするフラグの組み合わせ

VB

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応


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;

 

1-2.それぞれのフラグの意味

それぞれのフラグ単体のおおよその意味は次の通りです。

フラグ 意味
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

 

2.BindingFlagsで区別できないこと

2-1.メソッドやプロパティなどの種類

BindingFlagsではメソッド・プロパティなどの区別はできません。区別する方法は2つあります。

方法1:Typeクラスのメソッド

取得したいもの 名前を指定して1つ取得したい場合に使用するメソッド 複数取得したい場合に使用するメソッド 備考
すべてのメンバー GetMember GetMembers  
メソッド GetMethod GetMethods 普段隠蔽されている自動生成されたメソッドも含みます。
プロパティ GetProperty GetProperties  
イベント GetEvent GetEvents  
フィールド GetField GetFields クラス/構造体レベルの定数や変数です。
コンストラクター GetConstructor GetConstructors VBの場合、New を指します。
内部クラス/構造体 GetNestedType GetNestedTypes  

 

Typeの取得方法

Typeクラスのインスタンスを取得するには、GetType メソッドを使用します。

型からTypeクラスのインスタンスを取得するには、VBの場合GetType(xxx) 、C#の場合typeof(xxx)を使用します。

 

インスタンスからTypeを取得する例

VB

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

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

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

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");

 

 

方法2:MemberTypeプロパティ

GetMemberメソッドまたはGetMembersメソッドで取得できるMemberInfoクラスのMemberTypeプロパティを使ってこれらの種類を識別することもできます。

参考

https://docs.microsoft.com/ja-jp/dotnet/api/system.reflection.membertypes

 

2-2.ProtectedやFriend(internal)、Privateなどの詳細な区別

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メソッドを使用します。

 

2-3.プロパティやイベント向けに自動生成されたメソッド

プロパティやイベントを定義すると内部でメソッドが自動生成されます。これらのメソッドは普段隠蔽されていますがリフレクションを使うと存在が露呈します。

XXXプロパティから自動生成されるメソッドは get_XXX と set_XXX です。(GetまたはSetが定義されていない場合は対応するメソッドは生成されません。)

XXXイベントから自動生成されるメソッドは add_XXX と remove_XXX です。

これらの自動生成されたメソッドは IsSpecialNameプロパティが True になっています。

上記の他にオーバーロードされた演算子もIsSpecialNameがTrueです。

これを利用して、プログラマーによって明示的に定義されたメソッドのみの一覧から作成するには次のようにします。

この例ではStopWatchクラスのメソッドの名前の一覧を出力ウィンドウのデバッグに表示します。→ 表示される場所

VB

VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

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);
}

 

3.プログラム例

3-1.Focusという名前のPublicなメソッドを呼び出す

変数 obj で表されるオブジェクトの Public で定義されている Focus メソッドを取得して呼び出します。

VB

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

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);

 

3-2.型からそのメンバーの一覧を取得する

System.Diagnostics.Stopwatch型のメンバーの一覧を出力ウィンドウのデバッグに表示します。 → 表示される場所

例としてメンバーの名前やプロパティ・メソッドの区別・型など一部の情報を出力します。他にもこの例で扱っていない様々な情報が出力できます。

この例ではプロパティの情報はGetの方の情報を出力しています。GetとSetで違うスコープが設定されている場合などには対応していません。Getがないプロパティがあるとエラーになります。

VB

VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

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("");
}

 

 

3-3.コンストラクターを呼び出し、オブジェクトを生成する

この例ではSystem.IO.DriveInfoクラスの型情報からコンストラクラーを取得して、DriveInfoクラスのインスタンスを生成します。次にAvailableFreeSpaceプロパティを呼び出してCドライブの空き容量を取得します。→ 表示される場所

この例はあくまでリフレクションの参考にすることが目的であり、同じ機能を実現するだけならばリフレクションを使わないでDriveInfoクラスを操作するほうがはるかに簡単で優れています。

VB

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

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");

 

3-4.Privateなフィールドに値を設定する

以前私は自作のライブラリが新しい環境に対応できないという問題に直面し、ここで紹介するこの方法でPrivateなフィールドに値を設定することで回避できたことがあります。

データモデルのようなクラスの場合はリフレクションで値を読み書きする機能はしばしばありますが、自分でこの方法を使用するのはあまり良い手ではなく、何かの逃げの場合が多いように思います。

VB.NET2002対応 VB.NET2003対応 VB2005対応 VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

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());