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:
Include inherited members in the search.
No binding flags are used.
Include all static members in the inheritance hierarchy in the search (do not include static members of nested types in the search).
Perform a case-insensitive search.
Include instance members in the search.
Include nonpublic members in the search.
Include public members in the search.
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.
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(" Member Name: " + members[counter].ToString( )); Console.WriteLine(" Member Type: " + members[counter].MemberType); foreach (object attr in members[counter].GetCustomAttributes(false)) { Console.WriteLine(" Member attr: " + attr.ToString( )); } } } else { Console.WriteLine(" No 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.
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(MemberInfomember
, Objectcriteria
) { 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 Recipe 7.7; see the “Delegate Class” and “Type.FindMembers Method” topics in the MSDN documentation.