ChristopherIsFun
ChristopherIsFun

Reputation: 93

See if MemberInfo matches BindingFlags using C#

I need to see if a MemberInfo matches a particular BindingFlags. The closest method to this is Type#GetMember(string, BindingFlags).

I cannot find any method to do this.

I want to do something like this:

private List<MemberInfo> _members;

public IEnumerable<MemberInfo> GetMembers(BindingFlags flags)
{
    foreach(var member in _members)
    {
        if(member.MatchesFlags(flags))
        {
             yield return member;
        }
    }
}

Upvotes: 2

Views: 473

Answers (1)

CSDev
CSDev

Reputation: 3235

Actual types of MemberInfo obtained with reflection (e.g. via calling MemberInfo[] GetMembers() on Type) are:

  • System.Reflection.RuntimeMethodInfo
  • System.Reflection.RuntimeConstructorInfo
  • System.Reflection.RuntimePropertyInfo
  • System.Reflection.RtFieldInfo

All of them independently have property BindingFlags of type BindingFlags. But the property is NonPublic. And the types are internal sealed, that makes them inaccessible in normal code. Here reflection comes to rescue. To get BindingFlags of a MemberInfo one can use such an extension:

public static class MemberInfoExtension
{
    private static readonly Dictionary<MemberInfo, BindingFlags> cache =
        new Dictionary<MemberInfo, BindingFlags>();
    private static readonly BindingFlags flags =
        BindingFlags.Instance | BindingFlags.NonPublic;

    public static BindingFlags GetFlags(this MemberInfo memberInfo)
    {
        if (cache.TryGetValue(memberInfo, out var bindingFlags))
            return bindingFlags;

        return cache[memberInfo] =
            (BindingFlags)memberInfo.GetType()
                                    .GetProperty("BindingFlags", flags)
                                    .GetValue(memberInfo);
    }
}

To find matches with BindingFlags the following helper methods can be used:

public static class BindingFlagsExtension
{
    public static bool Contains(this BindingFlags flags, BindingFlags bindingFlags) =>
        (flags & bindingFlags) == bindingFlags;

    public static bool MatchesExactly(this BindingFlags flags, BindingFlags bindingFlags) =>
        flags == bindingFlags;

    public static bool MatchesPartly(this BindingFlags flags, BindingFlags bindingFlags) =>
        (flags & bindingFlags) != 0;
}

To test the solution one can use a class like this:

private class DemoClass
{
    private int PrivateInstanceField;
    public string PublicInstanceField;
    private bool PrivateInstanceProperty { get; }
    public object PublicInstanceProperty { get; }
    private void PrivateInstanceMethod() { }
    public void PublicInstanceMethod() { }
    private static int PrivateStaticField;
    public static string PublicStaticField;
    private static bool PrivateStaticProperty { get; }
    public static object PublicStaticProperty { get; }
    private static void PrivateStaticMethod() { }
    public static void PublicStaticMethod() { }
}

Then running:

static void Main(string[] args)
{
    var type = typeof(DemoClass);
    var members = type.GetMembers();
    var flags = BindingFlags.Public | BindingFlags.Instance;

    Console.WriteLine($"{type.Name} members with flags containing: {flags}\n");
    foreach (var m in members.Where(m => m.GetFlags().Contains(flags)))
        Print(m);

    Console.WriteLine($"\n{type.Name} members with flags matching exactly: {flags}\n");
    foreach (var m in members.Where(m => m.GetFlags().MatchesExactly(flags)))
        Print(m);

    Console.WriteLine($"\n{type.Name} members with flags matching partly: {flags}\n");
    foreach (var m in members.Where(m => m.GetFlags().MatchesPartly(flags)))
        Print(m);
}

private static void Print(MemberInfo memberInfo) =>
    Console.WriteLine($"\t{memberInfo.GetType().Name} {memberInfo} - {memberInfo.GetFlags()}");

gives:

DemoClass members with flags containing: Instance, Public

        RuntimeMethodInfo System.Object get_PublicInstanceProperty() - Instance, Public
        RuntimeMethodInfo Void PublicInstanceMethod() - Instance, Public
        RuntimeMethodInfo Boolean Equals(System.Object) - DeclaredOnly, Instance, Public
        RuntimeMethodInfo Int32 GetHashCode() - DeclaredOnly, Instance, Public
        RuntimeMethodInfo System.Type GetType() - DeclaredOnly, Instance, Public
        RuntimeMethodInfo System.String ToString() - DeclaredOnly, Instance, Public
        RuntimeConstructorInfo Void .ctor() - Instance, Public
        RuntimePropertyInfo System.Object PublicInstanceProperty - Instance, Public
        RtFieldInfo System.String PublicInstanceField - Instance, Public

DemoClass members with flags matching exactly: Instance, Public

        RuntimeMethodInfo System.Object get_PublicInstanceProperty() - Instance, Public
        RuntimeMethodInfo Void PublicInstanceMethod() - Instance, Public
        RuntimeConstructorInfo Void .ctor() - Instance, Public
        RuntimePropertyInfo System.Object PublicInstanceProperty - Instance, Public
        RtFieldInfo System.String PublicInstanceField - Instance, Public

DemoClass members with flags matching partly: Instance, Public

        RuntimeMethodInfo System.Object get_PublicInstanceProperty() - Instance, Public
        RuntimeMethodInfo Void PublicInstanceMethod() - Instance, Public
        RuntimeMethodInfo System.Object get_PublicStaticProperty() - Static, Public
        RuntimeMethodInfo Void PublicStaticMethod() - Static, Public
        RuntimeMethodInfo Boolean Equals(System.Object) - DeclaredOnly, Instance, Public
        RuntimeMethodInfo Int32 GetHashCode() - DeclaredOnly, Instance, Public
        RuntimeMethodInfo System.Type GetType() - DeclaredOnly, Instance, Public
        RuntimeMethodInfo System.String ToString() - DeclaredOnly, Instance, Public
        RuntimeConstructorInfo Void .ctor() - Instance, Public
        RuntimePropertyInfo System.Object PublicInstanceProperty - Instance, Public
        RuntimePropertyInfo System.Object PublicStaticProperty - Static, Public
        RtFieldInfo System.String PublicInstanceField - Instance, Public
        RtFieldInfo System.String PublicStaticField - Static, Public

Upvotes: 3

Related Questions