- 1. InvokeMember
- 1.1 InvokeMember Parameters
- 1.2 Practical Use of InvokeMember and Member Overloading
- 1.2.1 Static Methods and Instance Methods
- 1.2.2 Method Parameters
- 1.2.3 Field Properties
- 1.2.4 Default Members
- 1.2.5 Method ref and out Parameters
- 1.2.6 Create Instance
- 1.2.7 Access Members
- 1.2.8 Invoke Private Methods
- 1.2.9 Private Properties
- 1.2.10 Private Properties of Parent Class
- 1.2.11 GetGetMethod() and SetGetMethod() of Properties
- 1.2.12 GetAccessors
The "C# Reflection and Attributes" series has completed seven articles, explaining the usage and practical applications of reflection. The sixth and seventh articles summarized practical exercises related to reflection attributes, allowing learners to apply knowledge to solve common real-world problems.
Earlier discussions focused on foundational concepts and practice, ensuring that readers acquire basic knowledge after completion. This article extends some earlier topics, addressing unresolved issues and further exploring special scenarios.
This article supplements certain operational details, introducing common operational cases and demonstrations of reflection using another format.
This series has now reached its eighth article, with the next focusing on performance metrics for various reflective operations.
If there are reflection operations you wish to know about that have not been covered in this series by the end of this article, feel free to contact the author, and these will be included in future writings.
Given that this article contains numerous sections, it is recommended to save it for later reading. 😄
1. InvokeMember
Invoke a specified member (CultureInfo) using a specified binding constraint and matching parameters list along with culture.
The definition of this method can be somewhat opaque; don’t worry, just continue reading.
Previously, we used MemberInfo to obtain members of types and perform operations, also using PropertyInfo, MethodInfo, etc. The members we've accessed have all been public.
The InvokeMember
method allows us to conveniently invoke members of static or instance objects, including private members, indexers, and more.
There are primarily two overloads of InvokeMember:
public object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args);
public object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args, CultureInfo? culture);
Note: InvokeMember
cannot be used to invoke generic methods.
The parameters in InvokeMember can be complex, and we generally only use the first overload. The second overload adds a CultureInfo
parameter to handle language and cultural differences. This article will refer only to the first overload of InvokeMember.
1.1 InvokeMember Parameters
This section introduces the parameters of the InvokeMember method and their functions, and following the examples that appear throughout the article will help you quickly master the knowledge points.
1.1.1 name
This contains the name of the constructor, method, property, or field member to call, with case sensitivity taken into account.
1.1.2 invokeAttr
The invokeAttr parameter is of the BindingFlags enumeration, which allows us to restrict the information about the members to call.
For example, to access private members use BindingFlags.NonPublic
, and for static members use BindingFlags.Static
. We can use these enum combinations to filter out the required members.
1.1.3 binder
This is usually null and rarely used. The author is also not very clear on this.
The binder object defines a set of properties and enables binding, which may involve selecting overloaded methods, coercing parameter types, and invoking members via reflection.
1.1.4 target
This is the object on which to invoke the specified member.
If invoking a static member or a static member of an instance, target should be null; if invoking an instance member, this parameter should be the instance object.
1.1.5 args
These are the parameters passed, such as arguments for methods, or values for properties and fields.
1.1.6 Return
If calling a method or accessing a field value, there will be a return value; if calling a void
method or setting a property field, then it returns null
.
1.1.7 BindingFlags
This is an enumeration value specifying the flags that control binding and how to search for members and types through reflection.
The table below enumerates the common enumeration values applicable in typical scenarios, which can serve as notes for reference later.
Enumeration | Value | Explanation |
---|---|---|
CreateInstance | 512 | Indicates that reflection should create an instance of the specified type |
DeclaredOnly | 2 | Indicates that only members declared at the provided type level in the hierarchy should be considered |
Default | 0 | Indicates no binding flags are defined |
FlattenHierarchy | 64 | Indicates that public members and protected static members in the hierarchy should be returned. Does not return private static members in derived classes. Static members include fields, methods, events, and properties. Nested types are not supported. |
GetField | 1024 | Obtains the value of a field |
GetProperty | 4096 | Obtains the value of a property |
IgnoreCase | 1 | Indicates that member name case should be ignored during binding |
IgnoreReturn | 16777216 | Used in COM interop to indicate that the return value of a member can be ignored |
Instance | 4 | Obtains instance members |
InvokeMethod | 256 | Invokes methods |
NonPublic | 32 | Obtains non-public members |
Public | 16 | Obtains public members |
SetField | 2048 | Sets the value of a field |
SetProperty | 8192 | Sets the value of a property |
Static | 8 | Obtains static members |
SuppressChangeType | 131072 | Not implemented |
The above enums can be combined to filter out the required members.
1.1.8 Based on Visibility
- Specify
BindingFlags.Public
to include public members in the search. - Specify
BindingFlags.NonPublic
to include non-public members (i.e., private members, internal members, and protected members) in the search. - Specify
BindingFlags.FlattenHierarchy
to include static members in the hierarchical structure.
1.1.9 Case Sensitivity and Search Hierarchy
The following BindingFlags modifiers can be used to alter the search behavior:
BindingFlags.IgnoreCase
ignores case sensitivity ofname
.BindingFlags.DeclaredOnly
only searches members declared at the type level, excluding inherited members.
For DeclaredOnly
, refer to section 1.4 of "C# Reflection and Attributes (Part Five): Type Member Operations".
1.1.10 Specify Operations on Members
The following BindingFlags call flags can indicate what operations to perform on members:
-
CreateInstance
calls a constructor (thereforename
will be ignored, as constructors do not require a name); -
InvokeMethod
invokes a method (without calling a constructor); -
GetField
retrieves a field's value; -
SetField
sets a field's value; -
GetProperty
retrieves a property’s value; -
SetProperty
sets a property’s value;
Additionally, some operations may conflict, such as InvokeMethod
with SetField
or SetProperty
.
If InvokeMethod
is used alone, it will implicitly include BindingFlags.Public
, BindingFlags.Instance
, and BindingFlags.Static
. This is an important note.
1.2 Practical Use of InvokeMember and Member Overloading
This section introduces the use of InvokeMember and the use of BindingFlags with MethodInfo, PropertyInfo, and others.
Before to this, create a class type:
public class MyClass
{
}</code></pre>
In the Main method, retrieve the Type and instantiate it:
Type type = typeof(MyClass);
object example = Activator.CreateInstance(type);
1.2.1 Static Methods and Instance Methods
Add two methods to MyClass, one static and one instance method:
public static void A()
{
Console.WriteLine("A() method has been called");
}
public void B()
{
Console.WriteLine("B() method has been called");
}
Call them using InvokeMember:
type.InvokeMember("A", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new object[] { });
type.InvokeMember("B", BindingFlags.InvokeMethod, null, example, new object[] { });
type.GetMethod("A").Invoke(null, null);
type.GetMethod("B").Invoke(example, null);</code></pre>
The first invocation calls the static method A
, the second calls the instance method B
, while the third and fourth use MethodInfo to execute methods.
If a method has no parameters, you may use new object[] { }
or null
.
When calling methods with InvokeMember
, static methods use BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static
; instance methods use BindingFlags.InvokeMethod
.
However, using BindingFlags.InvokeMethod | BindingFlags.Public
for instance methods will throw an error. Why is that?
1.2.2 Method Parameters
Passing parameters to methods is simple using new object[] { }
.
For instance:
public void Test(string a, string b)
{
Console.WriteLine(a + b);
}
Type type = typeof(MyClass);
object example = Activator.CreateInstance(type);
type.InvokeMember("Test", BindingFlags.InvokeMethod, null, example, new object[] { "666","666" });</code></pre>
You can also specify argument names when calling the method.
Example:
// Normal instantiation call
(new MyClass()).Test(b: "前", a: "后");
// Parameter values
var parmA = new object[] { "前", "后" };
// Specify parameter names
var parmB = new string[] { "b", "a" };
type.InvokeMember("Test", BindingFlags.InvokeMethod, null, example, parmA, null, null, parmB);</code></pre>
1.2.3 Field Properties
In BindingFlags:
GetField
obtains the value of a field;
SetField
sets the value of a field;
GetProperty
obtains the value of a property;
SetProperty
sets the value of a property;
Add the following code to MyClass:
public string C = "c";
public string D { get; set; }
In Main, use:
type.InvokeMember("C",BindingFlags.SetField,null,example,new object[] { "666"});
Console.WriteLine(type.InvokeMember("C", BindingFlags.GetField ,null, example, null));
type.InvokeMember("D", BindingFlags.SetProperty, null, example, new object[] { "666" });
Console.WriteLine(type.InvokeMember("D", BindingFlags.GetProperty, null, example, null));</code></pre>
If you are unsure whether it is a property or method, you can use BindingFlags.GetField | BindingFlags.GetProperty
to try both.
1.2.4 Default Members
A class can be marked with a default member using the DefaultMemberAttribute
. It can be invoked using BindingFlags.Default
.
[DefaultMemberAttribute("TestA")]
public class MyClass
{
public void TestA(string a, string b)
{
Console.WriteLine(a + b);
}
public void TestB(string a, string b)
{
Console.WriteLine(a);
}
}
Type type = typeof(MyClass);
object example = Activator.CreateInstance(type);
type.InvokeMember(string.Empty, BindingFlags.InvokeMethod | BindingFlags.Default, null, example, new object[] { "666", "666" });</code></pre>
At this point, there's no need to pass the name parameter.
1.2.5 Ref and Out Parameters of Methods
I forgot to mention the situation when method parameters are ref or out in the previous seven sections, so I'll add it here.
When the parameters are ref or out, MethodInfo
can be called like this.
The usage is straightforward: no special attributes are required, and it can be called directly.
public void Test(ref string a, ref string b)
{
Console.WriteLine($"Before interaction, a={a},b={b}");
string c = a;
b = a;
a = c;
}
Type type = typeof(MyClass);
object example = Activator.CreateInstance(type);
string[] list = new string[] { "1", "2" };
MethodInfo method = type.GetMethod("Test");
method.Invoke(example, list);
Console.WriteLine($"After swapping, a={list[0]},b={list[1]}");</code></pre>
1.2.6 Creating Instances
In previous sections, we discussed instantiating types using Activator.CreateInstance
and via constructors. Now, types can also be instantiated using InvokeMember.
object example = type.InvokeMember("MyClass", BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new object[] { });
BindingFlags.Instance
indicates that the returned object is an instance, and BindingFlags.CreateInstance
indicates that this operation is for instantiating a type.
If the constructor has parameters, then include the arguments in new object[] { }
.
1.2.7 Accessing Members
Previously, we used the GetMembers()
method to retrieve all members of a type, and the method we used was an overload with no parameters. There is an overload method that uses BindingFlags as follows:
public abstract MemberInfo[] GetMembers(BindingFlags bindingAttr);
By utilizing BindingFlags, we can acquire specific members.
Type type = typeof(List<int>);
MemberInfo[] memInfo = type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
for (int i = 0; i < memInfo.Length; i++)
{
Console.WriteLine(memInfo[i].Name);
}</code></pre>
The aforementioned BindingFlags— BindingFlags.DeclaredOnly
fetches members defined in the current type (not inherited), BindingFlags.Instance
gets instance members (i.e., those that return results), and BindingFlags.Public
retrieves public members.
1.2.8 Invoking Private Methods
Using BindingFlags, we can conveniently access and execute private methods of a type.
public class MyClass
{
private string Test(string a, string b)
{
return a + b;
}
private void WriteLine(string message)
{
Console.WriteLine(message);
}
}
Type type = typeof(MyClass);
object example = Activator.CreateInstance(type);
MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.NonPublic);
for (int i = 0; i < methods.Length; i++)
{
Console.WriteLine(methods[i].Name);
}
MethodInfo method = methods.FirstOrDefault(x => x.Name == "WriteLine");
method.Invoke(example, new object[] { "Printed output" });
The parameters above specify fetching both public and non-public member methods defined in the current type (excluding inherited members, such as the ToString() method), ultimately returning an instance.
Whether public or private, as long as you obtain MethodInfo
, you can perform operations normally.
1.2.9 Private Properties
Accessing private properties is just as simple as accessing private methods:
public class MyClass
{
///
/// This property is meaningless
///
private string A { get; set; }
/// <summary>
/// This property will throw an error
/// </summary>
// private string B{ get; private set; }
public string C { get; private set; }
}</code></pre>
Type type = typeof(MyClass);
object example = Activator.CreateInstance(type);
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance);
foreach (var item in properties)
{
Console.WriteLine(item.Name);
}
PropertyInfo property = properties.FirstOrDefault(x=>x.Name=="A");
property.SetValue(example,"666");
Console.WriteLine(property.GetValue(example));
property = properties.FirstOrDefault(x=>x.Name=="C");
property.SetValue(example,"777");
Console.WriteLine(property.GetValue(example));
Regardless of being a public property or a private property, or even a private constructor, as long as you obtain MethodInfo
, you can operate normally.
1.2.10 Private Properties of Parent Classes
Just directly code it.
Create a type
public class A
{
private string Test { get; set; }
}
public class B : A
{
}
public class C : B
{
}</code></pre>
Type type = typeof(C);
PropertyInfo property;
object example;
while (true)
{
Console.WriteLine($"Searching for {type.Name}");
property = type.GetProperties(
BindingFlags.NonPublic |
BindingFlags.Instance)
.FirstOrDefault(x => x.Name == "Test");
// Found it
if (property != null)
{
example = Activator.CreateInstance(type);
break;
}
// Search upwards
if (type.BaseType == null)
throw new NullReferenceException("Cannot find it");
type = type.BaseType;
}
property.SetValue(example, "Setting property value");
Console.WriteLine(property.GetValue(example));
The loop above continuously searches for the property Test
until it is found.
1.2.11 GetGetMethod() and SetGetMethod() of Properties
After obtaining the PropertyInfo
of the private property above, you can set and get values using SetValue
and GetValue
.
Through GetGetMethod()
and SetGetMethod()
, you can also achieve the above operations.
The principle is that when compiling properties, two methods are generated.
public class MyClass
{
private string Test { get; set; }
}
Type type = typeof(MyClass);
object example = Activator.CreateInstance(type);
// GetProperty() will throw an error and cannot access the property
// type.GetProperty("Test", BindingFlags.DeclaredOnly |BindingFlags.NonPublic |BindingFlags.Instance);
// Accessing the private property
PropertyInfo property = type.GetProperties(
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance)
.FirstOrDefault(x => x.Name == "Test");
// nonPublic: true to access private methods
MethodInfo set = property.GetSetMethod(nonPublic: true);
set.Invoke(example, new object[] { "Test" });
MethodInfo get = property.GetGetMethod(nonPublic:true);
// Get property value
Console.WriteLine(get.Invoke(example, null));
// Get property value
Console.WriteLine(property.GetValue(example));</code></pre>
Because GetGetMethod()
and SetGetMethod()
acquire methods, followed by invoking them via Invoke
, it's said that this has better performance.
Of course, if you change the property definition to the following, it still holds.
public string Test { get;private set; }
1.2.12 GetAccessors
In the previous section, "C# Reflection and Attributes (Five): Type Member Operations," section 2.2 introduced this method. Now, let's complete the operations for reading and setting property values using GetAccessors()
.
public class MyClass
{
public string A { get; set; }
public string B { get; private set; }
private string C { get; set; }
}
Get all properties
Type type = typeof(MyClass);
object example = Activator.CreateInstance(type);
// GetProperty() will throw an error and cannot retrieve the property
// type.GetProperty("Test", BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Instance);
// Retrieve private properties
PropertyInfo[] properties = type.GetProperties(
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);</code></pre>
Start operations
// Loop through all properties and call setters/getters
foreach (var item in properties)
{
MethodInfo[] methods = item.GetAccessors(nonPublic: true);
// Set method, Get method
MethodInfo mSet = null;
MethodInfo mGet = null;
Console.WriteLine("\nProperty " + item.Name);
// Actually, each property has only two methods, no need to use foreach
foreach (var itemNode in methods)
{
// If there is no return value, it means it's a method like Void set_B(System.String)
// i.e., the setter
if (itemNode.ReturnType == typeof(void))
{
Console.WriteLine("Setter " + itemNode);
Console.WriteLine("Is public " + itemNode.IsPublic);
mSet = itemNode;
}
else
{
Console.WriteLine("Getter " + itemNode);
Console.WriteLine("Is public " + itemNode.IsPublic);
mGet = itemNode;
}
}
// Assign and read values
mSet.Invoke(example, new object[] { "Set Value" });
Console.WriteLine("Retrieved value " + mGet.Invoke(example, null));
}</code></pre>
文章评论