DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 7.8 An Advanced Member Search Mechanism

Problem

You are searching for a member within a type using the Type class. However, complex member searches are not available through the GetMember and GetMembers methods of a Type object. The GetMember method searches for a member name only within a type limited by the set of BindingFlags used, and the GetMembers method searches for all members limited by the set of BindingFlags used. BindingFlags is an enumeration of various member types that can be searched. The BindingFlags related to this recipe are defined here:


DeclaredOnly

Include inherited members in the search.


Default

No binding flags are used.


FlattenHierarchy

Include all static members in the inheritance hierarchy in the search (do not include static members of nested types in the search).


IgnoreCase

Perform a case-insensitive search.


Instance

Include instance members in the search.


NonPublic

Include nonpublic members in the search.


Public

Include public members in the search.


Static

Include static members in the search.

You need to create more flexible and advanced searches for members that do not involve creating your own member search engine.

Solution

The FindMembers method of a Type object can be used, along with a callback, to create your own complex searches. The following method will call our custom member searching method, SearchMembers:

using System;
using System.Reflection;

public class SearchType
{
    public void TestSearchMembers( )
    {
        MemberInfo[] members = SearchMembers(this.GetType( ), 
                                             Type.GetType("System.Int32"));

        if (members.Length > 0) 
        {
            Console.WriteLine("Matches found:"); 

            // Display information for each match  
            for(int counter = 0; counter < members.Length; counter++)
            {
                Console.WriteLine("\tMember Name: " + 
                                  members[counter].ToString( ));
                Console.WriteLine("\tMember Type: " + 
                                  members[counter].MemberType);
                foreach (object attr in 
                         members[counter].GetCustomAttributes(false))
                {
                    Console.WriteLine("\t\tMember attr: " + 
                      attr.ToString( ));
                }
            }
        }
        else
        {
            Console.WriteLine("\t\tNo matches found");
        }
    }

    public MemberInfo[] SearchMembers(Type searchedType, Type returnType)
    {
        // Delegate that compares the member's return type 
        //    against the returnType parameter
        MemberFilter filterCallback = new MemberFilter(ReturnTypeFilter);
    
        MemberInfo[] members = searchedType.FindMembers(MemberTypes.All, 
                    BindingFlags.Instance | BindingFlags.Public | 
                    BindingFlags.NonPublic | BindingFlags.Static, 
                    filterCallback, 
                    returnType);

        return (members);
    }

    private bool ReturnTypeFilter(MemberInfo member, object criteria)
    {
        // Obtain the return type of either a method or property
        string returnType = "";
        if (member is MethodInfo)
        {
            returnType = ((MethodInfo)member).ReturnType.FullName;
        }
        else if (member is PropertyInfo)
        {
            returnType = ((PropertyInfo)member).PropertyType.FullName;
        }
        else
        {
            return (false);
        }
    
        // Match return type
        if (returnType == ((Type)criteria).FullName)
        {
            return (true);
        }
        else
        {
            return (false);
        }
    }
}

This method will search for any member in the current type that has a return value of System.Int32.

The SearchMembers method accepts a Type object in which to search and a string representation of the full name of a return type. This method simply calls the FindMembers method of the searchType object passed to it. Notice that the returnType parameter is passed to the FindMembers method as the last parameter.

The MemberFilter delegate, filterCallback, defines the ReturnTypeFilter method to be called for each member that meets the specified criteria of the FindMembers method (i.e, MemberTypes.All, BindingFlags.Instance, BindingFlags.Public, BindingFlags.NonPublic, and BindingFlags.Static). The real power of this search mechanism lies in the ReturnTypeFilter callback method.

This callback method casts the member parameter to the correct member type (i.e., MethodInfo or PropertyInfo), obtains the return type, and compares that return type to the one passed in to the returnType parameter of the SearchMembers method. A return value of true indicates that the return types matched; a false indicates they did not match.

Discussion

Most complex member searches can be performed only through the use of the FindMembers method of a Type object. This method returns an array of MemberInfo objects that contain all members that match the memberType, bindingAttr, and filterCriteria parameters.

This method makes use of the MemberFilter delegate, which is passed in to the filter parameter. This delegate is supplied by the FCL and allows an extra layer of member filtering to occur. This filtering can be anything you want. This delegate returns a Boolean value, where true indicates that the member object passed in to this delegate should be included in the MemberInfo array that the FindMembers method returns, and false indicates that this member object should not be included.

There are many ways to use this MemberFilter delegate to search for members within a type. Here are just a few other items that can be searched for:

  • A filter callback to search for only fields marked as const:

    private bool ReturnTypeFilter(MemberInfo member, object criteria)
    {
        if (member is FieldInfo)
        {
            if (((FieldInfo)member).IsLiteral)
            {
                return (true);
            }
            else
            {
                return (false);
            }
        }
    
        return (false);
    }
  • A filter callback to search for only fields marked as readonly:

    private bool ReturnTypeFilter(MemberInfo member, object criteria)
    {
        if (member is FieldInfo)
        {
            if (((FieldInfo)member).IsInitOnly)
            {
                return (true);
            }
            else
            {
                return (false);
            }
        }
    
        return (false);
    }
  • A filter to search for a read-only property (note that in VB.NET, this filter finds methods marked with the readonly modifier, and in C#, this filter finds methods that only have a get accessor):

    private bool ReturnTypeFilter(MemberInfo member, object criteria)
    {
        if (member is PropertyInfo)
        {
            if (((PropertyInfo)member).CanRead && !((PropertyInfo)member).CanWrite)
            {
                return (true);
            }
            else
            {
                return (false);
            }
        }
    
        return (false);
    }
  • A filter to search for any methods that contain out parameters:

    private bool ReturnTypeFilter(MemberInfo member, object criteria)
    {
        if (member is MethodInfo)
        {
            ParameterInfo[] params = ((MethodInfo)member).GetParameters( );
            foreach (ParameterInfo param in params)
            {
                if (param.IsOut)
                {
                    return (true);
                    break;
                }
            }
    
            return (false);
        }
    
        return (false);
    }
  • A filter to search for any members that are marked with the System.ObsoleteAttribute attribute:

    private bool ReturnTypeFilter(MemberInfo member, object criteria)
    {
        object[] attrs = member.GetCustomAttributes(false);
        foreach (object attr in attrs)
        {
            if (attr.ToString( ).Equals("System.ObsoleteAttribute"))
            {
                return (true);
            }
        }
    
        return (false);
    }

    Creating a filter that searches for delegates or some ingredient of a delegate must be done in a roundabout way, as there is no DelegateInfo object, and the MemberTypes enumeration does not contain a value for delegates. A delegate type shows up as a nested type in the reflection API; this nested type's base class can then be checked to see whether it is a System.MulticastDelegate or a System.Delegate type (note that we can test the base class because both the MulticastDelegate and Delegate types are sealed). This is the code to determine whether a nested type is a delegate:

    private bool ReturnTypeFilter(MemberInfo member, Object criteria)
    {
        if (member.MemberType == MemberTypes.NestedType)
        {
            if (((Type)member).BaseType.ToString( ).Equals(
                     "System.MulticastDelegate") ||
                ((Type)member).BaseType.ToString( ).Equals("System.Delegate"))
            {
                return (true);
            }
    
            return (false);
        }
    
        return (false);
    }

See Also

See Recipe 7.7; see the "Delegate Class" and "Type.FindMembers Method" topics in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section