C# advanced learning -- Reflection

I Definition of reflection

The ability to review metadata and collect information about its types.

II Basic concepts

(1) Assembly: defines and loads an assembly, loads all modules in the assembly, finds a type from this assembly, and creates an instance of that type.

(2) Module: get the assembly containing the module and the classes in the module. You can also get all global methods or other specific non global methods defined on the module.

(3) ConstructorInfo: get the name, parameters, access modifiers (such as pulic or private) and implementation details (such as abstract or virtual) of the constructor.

(4) MethodInfo(GetMethod/GetMethods): get the name, return type, parameter, access modifier (such as pulic or private) and implementation details (such as abstract or virtual) of the method.

(5) Fieldinfo (getfield / getfields): get the field name, access modifier (such as public or private) and implementation details (such as static), and get or set the field value.

(6) EventInfo(GetEvent/GetEvents): get the event name, event handler data type, custom attribute, declaration type and reflection type, and add or remove event handlers.

(7) PropertyInfo(GetProperty/GetProperties): get the name, data type, declaration type, reflection type and read-only or writable state of the property, and get or set the property value.

(8) ParameterInfo: get the name, data type, input parameter or output parameter of the parameter, and the position of the parameter in the method signature.

(9) MemberInfo(GetMember/GetMembers): get fields, events, properties and other information

III Reflex action

Before demonstrating the role of reflection, we first define the following entity class, assuming that the entity class is located under a third-party class library, the class library name is "TestClass" and the class name is "Person"

    public class Person
    {
        private int id;
        public int Id { get => id; set => id = value; }
        public string Name { set; get; }
        public string Phone { get; set; }      
        public Person()
        {
           
        }
        public Person(string a, int b)
        {
            this.Name = a;
            this.Phone = b.ToString();
        }
        public Person(int id, string name, string phone)
        {
            this.Id = id;
            this.Name = name;
            this.Phone = phone;
        }        
        public string getName()
        {
            return this.Name;            
        }
        public string getName1(string str)
        {
            return str;
        }       
        public string getPhone()
        {
            return this.Phone;
        }
        public string getPhone(string str)
        {
            return this.Phone+str;
        }
        public string getPhone(string str,int num)
        {
            return this.Phone + str+num;
        }
        private void privateMethod(string a)
        {
            Console.WriteLine("This is a private method,The parameters passed in are:"+a);
        }  
    }

1. Create an object without parameters

The essence of creating an object without maturity is to call a parameterless constructor. The specific implementation is as follows

        /// <summary>
        ///Create an object without parameters
        /// </summary>
        /// <returns></returns>
        private static Person CreateWithoutParms()
        {
            Assembly assembly = Assembly.Load("TestClass");//Load assembly
            Type type = assembly.GetType("TestClass.Person");//Get class name (with namespace)
            object o = Activator.CreateInstance(type);//Create a Person entity without parameters
            Person person = o as Person;
            return person;
        }

Call in console

            Person person = CreateWithoutParms();
            person.Name = "Zhang San";
            Console.WriteLine("person.Name:"+ person.Name);
The returned results are as follows:

Successfully called and created Person, and called the parameterless constructor of Person

2. Create an object with parameters

The essence of creating a mature object is to call the constructor with parameters. The specific implementation is as follows

        /// <summary>
        ///Creating objects with parameters
        /// </summary>
        /// <returns></returns>
        private static Person CreateWithParms()
        {
            Assembly assembly = Assembly.Load("TestClass");//Load assembly
            Type type = assembly.GetType("TestClass.Person");//Get class name (with namespace)
            object o = Activator.CreateInstance(type, new object[] {"a",666 });//Create a Person entity with parameters
            Person person = o as Person;
            return person;
        }

Call in console

            Person person = CreateWithParms();
            Console.WriteLine("person.Name:"+person.Name+ " person.Phone:" + person.Phone);
The returned results are as follows:

Successfully called, created Person, and directly assigned value to the attribute by using the construction with parameters

####Note: if the constructor is private, you can set the nonPublic parameter in CreateInstance to true when creating an instance to create an instance using the private constructor

            object o = Activator.CreateInstance(type,true);

3. Call public methods

Using reflection to call the methods of the third-party class, you can use the obtained object to execute the methods in the object after obtaining the corresponding object through reflection. However, here, we mainly explain how to directly call the methods in the third-party class through reflection. The specific implementation is as follows

        /// <summary>
        ///Calling a method with parameters (no overloading)
        /// </summary>
        /// <returns></returns>
        private static string CallFunction()
        {
            Assembly assembly= Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type);
            MethodInfo methodInfo = type.GetMethod("getName1");
            string result=methodInfo.Invoke(o, new object[] { "This is an incoming parameter" }).ToString();
            return result;
        }

Call in console

            string rsult = CallFunction();
            Console.WriteLine(rsult);
The returned results are as follows:

Here we see that getName1 method is successfully called by reflection. It should be noted that getName1 method does not have any overload. If you need to call a method with overload, you need to use the following method. Here we assume that you need to call getPhone(string str,int num) method

        private static string CallFunctionWithOverload()
        {
            Assembly assembly = Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type);
            MethodInfo methodInfo = type.GetMethod("getPhone", new Type[] { typeof(string), typeof(int) });//Here, you need to pass the parameter type array to the GetMethod method
            string result=methodInfo.Invoke(o, new object[] { "This is incoming String parameter", 666 }).ToString();
            return result;
        }

Call in console

            string rsult = CallFunctionWithOverload();
            Console.WriteLine(rsult);
The returned results are as follows:
Through the above examples, we can see that the key to calling overloaded and non overloaded methods is whether to pass the type of parameters in GetMethod.

The following is a comprehensive example to call all methods in the Person class and output the results. If the parameter type is String, it will be passed to "AAA" by default. If the parameter type is Int, it will be passed to 666 by default. The implementation method is as follows:

        private static void CallAllFunction()
        {
            Assembly assembly = Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type);
            foreach (MethodInfo methodInfoItem in type.GetMethods())
            {
                Console.WriteLine("implement"+ methodInfoItem.Name+ "method");
                List<object> objectList = new List<object>();
                foreach (ParameterInfo parameterInfoItem in methodInfoItem.GetParameters())
                {
                    if (parameterInfoItem.ParameterType == typeof(String))
                    {
                        objectList.Add("AAA");
                    }
                    else if (parameterInfoItem.ParameterType == typeof(int))
                    {
                        objectList.Add(666);
                    }
                }
                try//Try is used here catch... This is to simplify the processing of program error caused by property acquisition failure
                {
                    string result = methodInfoItem.Invoke(o, objectList.ToArray()).ToString();
                    Console.WriteLine("The result is:" + result);
                }
                catch 
                {
                }
            }
        }

The returned results after calling are as follows:

Here we see that all the methods in Person have been executed, including all system methods

4. Call private method

        /// <summary>
        ///Call private method
        /// </summary>
        private static void CallPrivateFunction()
        {
            Assembly assembly = Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type);
            MethodInfo methodInfo = type.GetMethod("privateMethod", BindingFlags.Instance | BindingFlags.NonPublic);
            methodInfo.Invoke(o, new object[] { "Zhang San"});
        }
The returned results after calling are as follows:
Through the above examples, it is not difficult to find that the difference between calling public methods and private methods is whether "BindingFlags.Instance | BindingFlags.NonPublic" is set when calling the GetMethod method of type

5. Get and operate attributes

        /// <summary>
        ///Get and action properties
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="propertyValue"></param>
        private static void getAndSetProperity(string propertyName,string propertyValue)
        {
            //1. Create Person entity through reflection
            Assembly assembly = Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type, new object[] { "Zhang San", 131000000 });
            PropertyInfo propertyInfo=type.GetProperty(propertyName);
            Console.WriteLine("Before modification Phone: "+ propertyInfo.GetValue(o));//Get property value
            propertyInfo.SetValue(o, propertyValue);//Set attribute value
            Console.WriteLine("After modification Phone: " + propertyInfo.GetValue(o));           
        }
The returned results after calling are as follows:
Through the above examples, it can be found that the key methods to obtain and set properties are GetValue and SetValue respectively, and the key incoming parameters are entity classes obtained through reflection

6. Get and operate fields

        /// <summary>
        ///Get and action fields
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="fieldValue"></param>
        private static void getAndSetField(string fieldName, int fieldValue)
        {
            Assembly assembly = Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type, new object[] {1, "Zhang San", "131000000" });            
            FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            Console.WriteLine("Before modification id"+ fieldInfo.GetValue(o));
            fieldInfo.SetValue(o, fieldValue);
            Console.WriteLine("After modification id" + fieldInfo.GetValue(o));
        }
The returned results after calling are as follows:

The method of setting and operating fields is basically the same as that of setting and operating properties. It should be noted that when using the GetField method of type, if you get or set a private field, you need to set the accessible property of the method. In this example, it is set to "BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance"

Next, we will continue to study the role of reflection in generics. Before further research, we will first define the following generic class. Like the above entity class, suppose that the generic class is located under a third-party class library with the name of "TestClass" and the class name of "GenericClass"

    public class GenericClass<X,Y,Z>
    {
        public X xxx{ set; get; }
        public Y yyy { set; get; }
        public Z zzz { set; get; }
        public void PrintParm<A,B,C>(A a, B b, C c)
        {
            Console.WriteLine("A The type of is" + a.GetType().Name + ",Value is" + a.ToString());
            Console.WriteLine("B The type of is" + b.GetType().Name + ",Value is" + b.ToString());
            Console.WriteLine("C The type of is" + c.GetType().Name + ",Value is" + c.ToString());
        }
    }

7. Create a generic class and call

        /// <summary>
        ///Calling a method in a generic class
        /// </summary>
        private static void GenericWithParms()
        {
            Assembly assembly = Assembly.Load("TestClass"); 
            Type type = assembly.GetType("TestClass.GenericClass`3"); 
            type= type.MakeGenericType(new Type[] { typeof(string),typeof(int),typeof(DateTime)});
            object o = Activator.CreateInstance(type);
            MethodInfo methodInfo = type.GetMethod("PrintParm");
            methodInfo = methodInfo.MakeGenericMethod(new Type[] { typeof(string), typeof(int), typeof(DateTime) });
            methodInfo.Invoke(o, new object[] {"Zhang San",666,DateTime.Now});
        }
The returned results after calling are as follows:

For the above codes, the following explanations are made:

1).

This setting is required only when creating a generic class. The number is the total number of parameters of the generic class

2).

Before creating a generic entity, set the passed in parameter type through the MakeGenericType method of type

3).

Just like creating a generic class, you need to set the parameter type of the generic method before calling the generic method

4). If you call a generic method in a generic class, you do not need to set the parameter type of the generic method. On the contrary, if you call a generic method in a generic class, you do not need to set the number of generic class parameters or the parameter type

So far, the common ways of reflection are explained

Finally, in fact, all data tests can be Cloud server Let's have a look Tencent cloud Related services, the server bought as test data is very good, at least 38 a year

Added by Leadwing on Thu, 13 Jan 2022 03:47:10 +0200