C# Reflection and Attributes (Part Five): Type Member Operations

2020年1月11日 3249点热度 0人点赞 1条评论
内容目录

In the previous three articles, the basics of reflection and information objects were introduced. Reflection mainly applies to member objects such as constructors, properties, fields, methods, and events. The fourth article introduces the instantiation of types and event operations.

This article introduces type member operations and practical exercises.

Due to the extensive content, it is encouraged to practice hands-on.

Member Types

[Image 1 Source: "C# 7.0 in a Nutshell: 19.2 Reflection and Calling Members"] So, how do you get the corresponding members using Type?

[Image 2 Source: "C# 7.0 in a Nutshell: 19.2 Reflection and Calling Members"] The methods above have versions for getting a single member or multiple members.

All *Info instances will be cached by the reflection API upon first use, which helps optimize the performance of the API.

1. MemberInfo

MemberInfo can retrieve information about member properties and provides access to the member metadata.

The MemberInfo class is the abstract base class used to obtain information about all members (constructors, events, fields, methods, and properties) of a class.

As seen in Image 1, MemberInfo is the base class for all reflection types and provides basic functionality for all members.

Using GetMember() or GetMembers() can retrieve one or more members of a type.

GetMembers() returns all public members of the current type (and its base classes).

The GetMember method can retrieve a specific member by name. Since members (methods, properties, etc.) may be overloaded, this method returns an array.

For example

            MemberInfo[] members = type.GetMember(test);

1.1 Exercise - Get Type Members and Output Information

Create a type

    public class MyClass
    {
        private static string A { get; set; }
        public string B;
        public string C { get; set; }
    [Required] 
    public int Id { get; set; }

    [Phone] 
    public string Phone { get; set; }

    [EmailAddress]
    public string Email { get; set; }

    static MyClass()
    {
        A = 666;
    }

    public MyClass()
    {
        B = 666;
    }

    public MyClass(string message)
    {
        C = message;
    }

    public string Add(string a, string b)
    {
        return a + b;
    }
}</code></pre>

Print

            Type type = typeof(MyClass);
            MemberInfo[] members = type.GetMembers();
            foreach (var item in members)
            {
                Console.WriteLine(item.Name +     |      + item.MemberType);
            }

Output

get_C    |     Method
set_C    |     Method
get_Id    |     Method
set_Id    |     Method
get_Phone    |     Method
set_Phone    |     Method
get_Email    |     Method
set_Email    |     Method
Add    |     Method
GetType    |     Method
ToString    |     Method
Equals    |     Method
GetHashCode    |     Method
.ctor    |     Constructor
.ctor    |     Constructor
C    |     Property
Id    |     Property
Phone    |     Property
Email    |     Property
B    |     Field

1.2 MemberType Enumeration

There is a property named MemberType in MemberInfo that is an enumeration of MemberType.

The definition of the MemberType enumeration is as follows

Name Value Description
All 191 Specifies all member types
Constructor 1 Specifies that the member is a constructor
Custom 64 Specifies that the member is a custom member type
Event 2 Specifies that the member is an event
Field 4 Specifies that the member is a field
Method 8 Specifies that the member is a method
NestedType 128 Specifies that the member is a nested type
Property 16 Specifies that the member is a property
TypeInfo 32 Specifies that the member is a type

Where MemberType.All is defined as All = NestedType | TypeInfo | Property | Method | Field | Event | Constructor.

1.3 MemberInfo Get Member Method and Call

The following example retrieves method members through GetMembers and invokes them with parameters.

This is just a demonstration. The instantiation and invocation of methods will be discussed in Section 3 of this article.

            MemberInfo[] members = type.GetMembers();
            foreach (var item in members)
            {
                // If the member is a method
                if (item.MemberType == MemberTypes.Method)
                {
                    // Output the parameter list of this method: parameter type + parameter name
                    foreach (ParameterInfo pi in ((MethodInfo)item).GetParameters())
                    {
                        Console.WriteLine(Parameter: Type={0}, Name={1}, pi.ParameterType, pi.Name);
                    }
                    // If the method has two parameters, invoke it
                    if (((MethodInfo)item).GetParameters().Length == 2)
                    {
                        // Invoke a method and pass parameters
                        MethodInfo method = (MethodInfo)item;
                        Console.WriteLine(Invoking a method, output result:);
                        Console.WriteLine(method.Invoke(example, new object[] { 1, 2 }));
                    }
                }
            }

1.4 Get Inherited Method Information (DeclaringType and ReflectedType)

In MemberInfo, there are three properties to obtain type information:

  • MemberType indicates the function type of the member (e.g., field, property, method, etc.);
  • DeclaringType returns the defining type of the member;
  • ReflectedType returns the specific type from which GetMembers was called;

Since a method can be inherited or overridden, it is often necessary to understand related information to determine and call methods.

DeclaringType: If a type uses methods from its superclass or itself, it returns the source of that method;

ReflectedType: It returns the type from which members are obtained; that is, when a member instance is obtained from a Type, it returns the name of that Type;

Create two types

    /// 
    /// Base class
    /// 
    public class MyClassFather
    {
        /// 
        /// Override ToString()
        /// 
        /// 
        public override string ToString()
        {
            return base.ToString();
        }
    }
/// <summary>
/// Subclass
/// </summary>
public class MyClassSon : MyClassFather
{
}</code></pre>

In the console Program.Main, write

            Type typeFather = typeof(MyClassFather);
            Type typeSon = typeof(MyClassSon);
            Type typeObj = typeof(object);
            Type typeProgram = typeof(Program);
        // To save steps, we won't use MemberInfo
        MethodInfo methodObj = typeObj.GetMethod(ToString);
        MethodInfo methodFather = typeFather.GetMethod(ToString);
        MethodInfo methodSon = typeSon.GetMethod(ToString);
        MethodInfo methodProgram = typeProgram.GetMethod(ToString);

Print DeclaringType

            Console.WriteLine(methodObj.DeclaringType);
            Console.WriteLine(methodFather.DeclaringType);
            Console.WriteLine(methodSon.DeclaringType);
            Console.WriteLine(methodProgram.DeclaringType);

Output

System.Object
Mytest.MyClassFather
Mytest.MyClassFather
System.Object

Analysis:

MyClassFather overrides the ToString method, so the DeclaringType obtained is MyClassFather;

MyClassSon inherits MyClassFather and directly uses the parent class's ToString() method, so it returns MyClassFather;

The Program does not override ToString(), so it returns Object;

2. Reflection from IL

The author's knowledge of IL is very limited, so only some simple content will be listed.

In the earlier exercise, we found

        public string C { get; set; }

outputs

get_C    |     Method
set_C    |     Method
C    |     Property

The generated IL is as follows

.property instance string C()
{  
    .get instance string Mytest.MyClass::get_C()  
    .set instance void Mytest.MyClass::set_C(string)
} 

Summary of IL generated for properties, indexers, and events:

For the three types above, when generating IL, corresponding methods are created, and can be accessed using GetMethods() or GetMembers().

2.1 Get Properties Construct

Define a type

    public class MyClass
    {
        private string Test;
    public string A
    {
        get { return Test; }
    }

    public string B
    {
        set { Test = value; }
    }

    public string C { get; set; }
}</code></pre>

From previous examples, many are about retrieving the list of properties, but it was not possible to identify their constructions from them, for instance, in the MyClass type.

PropertyInfo has a method GetAccessors() which can obtain the corresponding information.

Method Description
GetAccessors() Returns an array containing the public get and set accessors reflected by the property currently being reflected.
GetAccessors(Boolean) Returns an array containing both public and non-public (if specified) get and set accessors reflected by the property currently being reflected.

Usage Example

            Type type = typeof(MyClass);
            PropertyInfo[] list = type.GetProperties();
            foreach (var item in list)
            {
                var c = item.GetAccessors();
                foreach (var node in c)
                {
                    Console.WriteLine(node);
                }
            Console.WriteLine(************);
        }</code></pre>

Output

System.String get_A()
************
Void set_B(System.String)
************
System.String get_C()
Void set_C(System.String)
************

If we change the property C to

        public string C { get; private set; }

then only the following will be output

System.String get_A()
************
Void set_B(System.String)
************
System.String get_C()
************

If you want to get private constructors, you can use .GetAccessors(true).

2.2 Property Methods

From reflection and IL, we know that a property automatically generates two methods.

We can obtain these methods through PropertyInfo.

Method Description
GetSetMethod Gets the set method, returns MethodInfo
GetGetMethod Gets the get method, returns MethodInfo
GetAccessors Obtains a collection of the above two methods, returns MethodInfo[]

Creating a Property

        public string C { get;  set; }
            Type type = typeof(MyClass);
            PropertyInfo property = type.GetProperty(C);
            // Specify whether to get get or set
            MethodInfo set = property.GetSetMethod();
            MethodInfo get = property.GetGetMethod();
        MethodInfo[] all = property.GetAccessors();</code></pre>

3. Method Operations

We must remember that reflection is the use of metadata; only instances can be executed and called.

Here, let's talk about the nameof keyword. The nameof has no effect; it will not impact the program in any way.

nameof(T) can output T, for example, nameof(Program) outputs Program.

So when is it useful?

When writing code, we use IDEs like Visual Studio, and using nameof with strongly typed types allows for finding references, jumping to definitions, retrieving comments, etc. If refactoring is needed, all references can be quickly renamed.

If we use strings directly, it's easy to misspell names, and once a name is modified, we need to manually find and change all instances where that string appears.

To call an instance method, the following steps are involved:

Step Type Description
Get Type Type Obtain Type via assembly or other means
Get Instance object Create an instance using Activator.CreateInstance(type);
Get Method MethodInfo or MemberInfo Retrieve the corresponding method via Type
Set Parameter List object[] parameters Parameters passed during method call
Execute Method .Invoke() method Executing MethodInfo.Invoke()
Get Return Result object Return result obtained after executing the method

3.1 Various Ways to Invoke Methods

Firstly, let's define a type

    public class MyClass
    {
        /// 
        /// No parameters, returns void
        /// 
        public void A()
        {
            Console.WriteLine(A被执行);
        }
    /// <summary>
    /// Has parameters, has return value
    /// </summary>
    /// <param name="a"></param>
    /// <returns></returns>
    public string B(string left)
    {
        Console.WriteLine(执行 B(string left));
        return left + 666;
    }

    public string C(string left,int right)
    {
        Console.WriteLine(执行 C(string left,int right));
        return left + right;
    }

    public string C(string left, string right)
    {
        Console.WriteLine(执行 C(string left, string right));
        return left + right;
    }
}</code></pre>

In Program, write code to obtain the Type and create an instance.

            Type type = typeof(MyClass);
        object example = Activator.CreateInstance(type);</code></pre>

3.1.1 Calling a Method

Let's call the method A()

            // Get A
            MethodInfo methodA = type.GetMethod(nameof(MyClass.A));
        // Pass instance and execute instance's A method
        methodA.Invoke(example, new Object[] { });</code></pre>

The method B has one parameter, so we add a parameter when invoking it

            object result;
        // Get B
        MethodInfo methodB = type.GetMethod(nameof(MyClass.B));

        // Pass parameter
        // Execute and get return result
        result = methodB.Invoke(example, new[] {测试});</code></pre>

3.1.2 Getting Parameter List

In section 1.1, there are examples of code for obtaining method parameters. We won't elaborate on that here.

3.1.3 Getting Overloaded Methods

In the fourth part of the series "C# Reflection and Attributes," we introduced calling and overloading for constructors ConstructorInfo, and MethodInfo is quite similar.

Above, we retrieved MethodInfo using type.GetMethod(MethodName). For MyClass.C, which has two overloads, we can specify which overload to use as follows.

            // Get C
            // Execute and get return result
            MethodInfo methodC = type.GetMethod(nameof(MyClass.C), new Type[] {typeof(string), typeof(string)});
            result = methodC.Invoke(example, new string[] {测试, 测试});
            //       result = methodC.Invoke(example, new Object[] {测试, 测试});

Thus, we have discussed the instantiation and operation of types, constructors, delegates, and methods.

Next, we will discuss how to set and retrieve values for properties and fields.

痴者工良

高级程序员劝退师

文章评论