C# reflection and its application

Before learning this blog, I suggest you learn about this blog: C# and. NET
Before learning reflection, you need to understand several concepts. What is reflection? What is metadata? What is Type? What is an assembly?

What is reflection? What is metadata?

The program is used to process data, and the program itself (class definition and class in BLC) is also data. The data about the program and its type is called Metadata and stored in the assembly. When a program is running, it can view the Metadata of other assemblies or its own. The behavior of a running program to view its own Metadata or the Metadata of other assemblies is called reflection.

What is Type?

For each Type used in the program, the CLR will create a Type object containing this Type information. Each Type used in the program is associated with an object of an independent Type class. No matter how many instances of the Type are created, only one Type object will be associated with all these instances.

What is an assembly?

An assembly is a version number, self explanatory, configurable binary file that can be hosted in the CLR. The extension of the assembly is exe or dll. An assembly is a collection of stored types. You can get all the type information in the assembly through the assembly.

Reflection, DLL, IL, Metadata, Assembly

The source code written in C # is compiled into an intermediate language (IL) conforming to the CLI specification. IL code and resources, such as bitmaps and strings, are stored in assemblies that typically have. DLL or. Exe extensions. The Assembly contains a list that describes the type, version and culture of the Assembly. These descriptive information is called Metadata. Reflection needs to find these Metadata in the Assembly, and then instantiate objects, call methods, read properties, etc.


Reflection API

1, Assembly

Note: there is no error loading dll. If there is no dependency loaded, an error will be reported when calling the dependent object

1.1,Load

30: The dll name has no suffix. Load the dll from the current directory. In the development environment Bin, the publisher is the current directory of the entry assembly file.

Assembly assembly = Assembly.Load("XXXX");

1.2,LoadFrom

30: Knowing the file name or path of the assembly, loads the assembly and automatically loads its dependent assemblies.

Assembly assembly = Assembly.LoadFrom("XXXX");

1.3,LoadFile

30: The full dll path will not be loaded incorrectly, and other assemblies referenced and dependent by the target assembly will not be loaded. You need to control and display the loading of all dependent assemblies. If there are no dependencies, you will make an error when using.

Assembly assembly = Assembly.LoadFile("XXXX");

2, Type instance

2.1. Nonparametric structure

After obtaining the Assembly, you can obtain the Type, and create an instance of the root Type. The created instance is of object Type. In order for the compiler to pass, Type conversion (XXX) is required

Type type = assembly.GetType("XXX.XXX");
object obj = (XXX)Activator.CreateInstance(type);

2.2 parametric structure

When creating an instance using Type, the parameterized constructor is carried by the new object [] parameter. This method will automatically match the constructor according to the Type in new object []

object obj = (XXX)Activator.CreateInstance(type, new object[] { "124", 123 });

2.3. Private structure (spark with single example)

As we all know, in order to avoid external instantiation in the code, the constructor is modified with the private modifier during implementation, so that it cannot be instantiated and called externally, but reflection can break this rule. The focus is on the CreateInstance method. The second parameter is true

Assembly assembly = Assembly.LoadFrom("XXXX");
Type type = assembly.GetType("XXX.XXX");
object obj = Activator.CreateInstance(type,true);

2.4. Generic classes

~n placeholder, representing several generic types. MakeGenericType defines the generic Type and passes in the Type array

Assembly assembly = Assembly.LoadFrom("XXXX");
Type type = assembly.GetType("XXX~n");
Type typeNew = type.MakeGenericType(new Type[] { typeof(int), typeof(XXX),typeof(string)  });
object obj = Activator.CreateInstance(typeNew, new object[] { "124", 123 });

3, Method call

3.1 general methods

Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX");// Type name
object instance = Activator.CreateInstance(type); // Instance objects according to type
MethodInfo method = type.GetMethod("XXX");// Method name
method.Invoke(instance,new object[] { "Method parameter 1", "Method parameter 2" });

3.2 static method

For static members, Invoke does not need to pass in the instance object, because there is only one static member in the class. After the Type is determined, there are already static members

Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX");// Type name
MethodInfo method = type.GetMethod("XXX");// Method name
method.Invoke(null,new object[] { "Method parameter 1", "Method parameter 2" });

3.3 private method

As we all know, private methods cannot be called externally in object-oriented programming language, but reflection can break this rule and call private methods. The focus is on the GetMethod method. The second parameter is BindingFlags.Instance|BindingFlags.NonPublic

Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX");// Type name
object instance = Activator.CreateInstance(type); // Instance objects according to type
MethodInfo method = type.GetMethod("XXX",BindingFlags.Instance|BindingFlags.NonPublic);// Method name
method.Invoke(instance, new object[] { "Method parameter 1", "Method parameter 2" });

3.4 generic methods

Both generic classes and generic methods need to specify the type of generic through the MakeGenericXXXX method. It should be noted that generic classes need placeholders when loading types, but generic methods do not

Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX~2");// Type name, ~2 placeholder, representing several generics
Type typeNew = type.MakeGenericType(new Type[] { typeof(string), typeof(int) });
object instance = Activator.CreateInstance(typeNew); // Instance objects according to type
MethodInfo method = type.GetMethod("XXX");// Method name
MethodInfo methodNew = method.MakeGenericMethod(new Type[] { typeof(int) });
methodNew.Invoke(instance, new object[] { "Method parameter 1", "Method parameter 2" });

3.5 application

After learning this, some students will certainly have questions. Is it meaningful to do so? Don't we call its method directly new in the program? The answer is: meaningful, then look down

In fact, I had such a question when I first worked. You must know MVC. If we enter IP: [controller] / [action] in the browser, we will access the business code for the background.

But how does it match our controller and action? Reflection is used here. When the program starts, it will scan the class of controller Type and cache its Type. When a request comes, it will find the Type reflection in the cache, instantiate it and call its method (i.e. action). Speaking of this, mvc's filter is to add something before and after the method is called (before and after the reflect invoke method).

4, Properties and fields

4.1. Attribute reading and writing

Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX.XXX");// Type name
object instance = Activator.CreateInstance(type); //Instance object
foreach (var prop in type.GetProperties())
{
    if (prop.Name.Equals("Id"))
    {
        prop.SetValue(instance, 1);
        Console.WriteLine(prop.GetValue(instance));
    }
    else if (prop.Name.Equals("Name"))
    {
        prop.SetValue(instance, "Zhang San");
        Console.WriteLine(prop.GetValue(instance));
    }
}

4.2. Field reading and writing

Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX.XXX");// Type name
object instance = Activator.CreateInstance(type); //Instance object
foreach (var field in type.GetFields())
{
    if (field.Name.Equals("id"))
    {
        field.SetValue(instance, 1);
        Console.WriteLine(field.GetValue(instance));
    }
    else if (field.Name.Equals("name"))
    {
        field.SetValue(instance, "Zhang San");
        Console.WriteLine(field.GetValue(instance));
    }
}

4.3 application

After learning this, some students may ask, what is the significance of reading and writing values of attribute fields? The answer is: of course, go on

Here are entity and entityDto. Students who know DDD should know what they do

public class Product
{
    public int ID { get; set; }
    public String Name { get; set; }
}
public class ProductDto
{
    public int ID { get; set; }
    public String Name { get; set; }
}

The following instantiates the data carried by entity, and assigns an entityDto in turn. This is a conventional writing method. If there are too many fields, do you still need to assign values manually in turn?

Product product = new Product()
{
    ID = 1,
    Name = "Zhang San"
};

ProductDto productDto = new ProductDto();
productDto.ID = product.ID;
productDto.Name = product.Name;

The answer to the question generated by entity dto is No. We can use reflection to automatically assign values between entities and entitydto to improve development efficiency.

In practical application, T will be used for encapsulation. This is just simple. Write the core code. Net also has a very mature framework, AutoMapper.

Product product = new Product()
{
    ID = 1,
    Name = "Zhang San"
};
Type productType = typeof(Product); // Product Type
Type productDtoType = typeof(ProductDto);// ProductDto Type
object productDto = Activator.CreateInstance(productDtoType); // ProductDto Instance
foreach (var prop in productDtoType.GetProperties())
{
    // Take the dto attribute name in turn, find it in the Product Type, and get the value from the Product Instance
    object val = productType.GetProperty(prop.Name).GetValue(product);
    // ProductDto Instance Set Propertie Val
    prop.SetValue(productDto,val);
}

performance testing

This is also a matter of concern. Most people on the Internet say that the performance is poor, which is a little one-sided. Object-oriented is static and reflection is dynamic, so there will be a little performance overhead.

Next, let's test. We instantiate the class and call the method of the object. Here, we analyze it 10 million times. Please see the following code

1. Here we define a class and a method

namespace ConsoleApp2
{
    public class Product
    {
        public int ID { get; set; }
        public String Name { get; set; } 

        public void PrintMsg()
        {

        }
    }
}

2. The sub table instantiates the class and calls the method in the form of static coding and reflection

static void Main(string[] args)
{
    Stopwatch stopwatchT = new Stopwatch();
    stopwatchT.Start();
    for (int i = 0; i < 10000000; i++)
    {
        Product product = new Product();
        product.PrintMsg();
    }
    stopwatchT.Stop();
    var tTime = stopwatchT.ElapsedMilliseconds;

    Stopwatch stopwatchR = new Stopwatch();
    stopwatchR.Start();
    for (int i = 0; i < 10000000; i++)
    {
        Assembly assembly = Assembly.Load("ConsoleApp2");// dll
        Type type = assembly.GetType("ConsoleApp2.Product");// Type name
        Product instance = (Product)Activator.CreateInstance(type); //Instance object
        instance.PrintMsg();
    }
    stopwatchR.Stop();
    var rTime = stopwatchR.ElapsedMilliseconds;

    Console.WriteLine($"Static:{tTime};Reflection:{rTime}");
}

3. After startup, it can be seen that the static coding takes 135ms and the reflection mode takes 32660ms. It can be seen that static coding is about 4409 times more efficient than reflection.

For static coding, we see that it takes only 135ms for 10 million, and 0.0000135ms for each. This is a little surprised at the time-consuming of new objects. In fact, as long as the new object does not do some business operations or occupy a lot of memory, you can rest assured that the new object.

In the reflection mode, we can see that its efficiency is indeed much lower than that of static coding reflection. Each reflection takes about 0.0032ms. This absolute value is very small and has little impact on the program. Therefore, although the efficiency of reflection is lower than that of static coding, the impact on the program at ordinary times can be said to be negligible.


4. In fact, there are some problems in our program, that is, the loading of Assembly and Type is a duplicate operation. We can trade space for time. We can put the process Assembly Type into the cache, as shown in the optimized program below

static void Main(string[] args)
{
    Stopwatch stopwatchT = new Stopwatch();
    stopwatchT.Start();
    for (int i = 0; i < 10000000; i++)
    {
        Product product = new Product();
        product.PrintMsg();
    }
    stopwatchT.Stop();
    var tTime = stopwatchT.ElapsedMilliseconds;

    Stopwatch stopwatchR = new Stopwatch();
    stopwatchR.Start();
    Assembly assembly = Assembly.Load("ConsoleApp2");// dll
    Type type = assembly.GetType("ConsoleApp2.Product");// Type name
    for (int i = 0; i < 10000000; i++)
    {
        Product instance = (Product)Activator.CreateInstance(type); //Instance object
        instance.PrintMsg();
    }
    stopwatchR.Stop();
    var rTime = stopwatchR.ElapsedMilliseconds;

    Console.WriteLine($"Static:{tTime};Reflection:{rTime}");
}

5. After startup, you can see that the reflection time is reduced to 588 milliseconds, about 55 times faster than before code optimization and only 5 times slower than static coding

At this time, the absolute value of reflection mode is very small, which is close to the time-consuming of static coding, and the loss of object performance can be almost ignored. When it comes to this, it does not mean that we respect reflection, but look at it objectively.

Some students may get to the point of saying that the performance is lower than static coding, so I advise you not to engage in development. Usually mvc, IOC, orm and so on use reflection a lot, which is related to reflection. You can taste your fine products.

If it is called hundreds of millions of times, it may affect the program performance. It will be optimized at that time. In fact, 95% of the program performance problems in normal development will not be used as reflection problems, so we should treat this problem objectively.


Advantages and disadvantages

The above describes the application of class, method, attribute and field related reflection. Some people wonder whether hard coding is better than reflection? The answer is: it's really better than reflection,

advantage
Dynamic: reflection is two words dynamic, just like MVC is to dynamically call methods. Databases or encapsulated algorithms for processing business logic can be dynamically switched using configuration files.

shortcoming
Coding is complex: you can also realize from the tutorial that you must have a deep understanding of the words used in daily work. Object-oriented static coding requires four or five lines of one or two lines of code.

Avoid compiler checking: we usually write code. In various ides, we write wrong, and error will prompt us when compiling. If there is no compiler to write code, we don't know how many mistakes we make. However, in reflection, the compiler will not check the class operation, which is also impossible to check. It should be that reflection is dynamic and runtime, unlike ordinary coding, which is static.

What is the value of reflection

The most intuitive difference of reflection is that it is converted from the existing fixed type to string operation, and does not need to be referenced in the project (reflection is dynamic and depends on string).

It should depend on strings, so that our program can be configured and easy to expand. Reflection (MVC, IOC, ORM, etc.) is widely used in normal framework development.

It is only rarely used in business logic development, which also needs to be experienced bit by bit in daily work experience. That's all for this article.


Tags: C#

Posted on Sat, 16 Oct 2021 00:22:28 -0400 by seeya