C# Reflection and Attributes (Part 1): Basics of Reflection

2019年12月29日 66点热度 0人点赞 2条评论
内容目录

1. Introduction

1.1 About Reflection and Attributes

In "C# 7.0 Essentials", this topic is covered in "Chapter 18 Reflection, Attributes, and Dynamic Programming"; in "C# 7.0 Core Technology Guide", it appears in "Chapter 19 Reflection and Metadata".

[Image from "C# 7.0 Essentials"]

Here we can obtain several highly relevant technologies: Reflection, Attributes, Metadata.

Metadata: Programs written in C# are compiled into an assembly, which contains metadata, compiled code, and resources. Metadata includes the following content:

  • A description of each type in the program or library;
  • Manifest information, including data related to the program itself and the libraries it depends on;
  • Custom attributes embedded in the code, providing additional information related to the constructs they annotate.

Reflection: The operation of examining and using metadata and compiled code at runtime is called reflection.

Contents included in an assembly:

[Image from "C# 7.0 Core Technology Guide"]

2. Assembly Operations

Code compiled from C# generates .dll or .exe files. We can manually load the assembly file using the Assembly class to perform various operations.

The Assembly class is located in the System.Reflection namespace.

The "C# 7.0 Core Technology Guide" lists common properties and methods of the Assembly class:

Next, we will learn how to use Assembly through code operations.

Create a console project and set assembly description information.

2.1 Getting the Assembly Object

Microsoft's official documentation recommends the following methods for loading assemblies:

  • The recommended method for loading an assembly is to use the Load method, which identifies the assembly to be loaded by its display name (for example, "b77a5c561934e089, Version = 2.0.0.0, Culture = neutral, PublicKeyToken ="). The search for the assembly follows the rules described in How the Runtime Locates Assemblies.
  • Using ReflectionOnlyLoad and ReflectionOnlyLoadFrom methods, you can load assemblies for reflection but not for execution. For example, you can inspect a 64-bit assembly from code running on a 32-bit platform.
  • In rare cases where assemblies must be identified by path, the LoadFile and LoadFrom methods can be provided.

There are generally three ways to get an assembly:

  • Assembly.Load()
  • Assembly.LoadFrom()
  • Assembly.LoadFile()

You can obtain currently referenced assemblies using the following method:

AppDomain.CurrentDomain.GetAssemblies();

Output:

System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e

ConsoleApp4, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

System.Runtime, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

System.Runtime.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

System.Console, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

2.1.1 Runtime Getting Assemblies

Retrieve assemblies through forms such as running types, functions, etc.

Assembly class:
        public static Assembly? GetAssembly(Type type);
    public static Assembly GetCallingAssembly();

    public static Assembly? GetEntryAssembly();

    public static Assembly GetExecutingAssembly();

Type class:
{type}.Assembly

Explanation of parsing:

Location Function Description
Assembly GetAssembly(Type) Gets the currently loaded assembly that defines the specified type
Assembly GetCallingAssembly() Returns the assembly of the method that calls the currently executing method
Assembly GetEntryAssembly() Gets the executable file of the process in the default application domain. This is the first executable file executed by ExecuteAssembly(String) in other application domains
Assembly GetExecutingAssembly() Gets the assembly that contains the currently executing code
Type Assembly Returns the assembly where a type is located

2.1.2 Usage Methods

            Assembly assem = typeof(Console).Assembly;
            Assembly ass = Assembly.GetExecutingAssembly();

2.1.3 Loading Assemblies from Files

Function Description
LoadFrom(String) Loads the assembly known by the file name or path
LoadFrom(String, Byte[], AssemblyHashAlgorithm) Loads the assembly using the given file name or path, hash value, and hash algorithm
LoadFrom(String, Evidence) Loads the assembly given the file name or path and provides security evidence
LoadFrom(String, Evidence, Byte[], AssemblyHashAlgorithm) Loads the assembly using the given file name or path, security evidence, hash value, and hash algorithm

2.1.4 Usage Methods

Assembly ass = Assembly.LoadFrom(@"X:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.0.0\ref\netcoreapp3.0\System.Console.dll");

Additionally, there are many other obscure ways to load assemblies, which are not necessary to list (because I won't).

2.2 Assembly Usage

After obtaining the Assembly object, various operations can be performed.

Common Assembly functions can be viewed in Figure 3.

First, set up two Assembly objects:

            Assembly assemA = typeof(Console).Assembly;
            Assembly assemB = Assembly.GetExecutingAssembly();

2.2.1 Getting the Fully Qualified Assembly Names

            Console.WriteLine("Fully Qualified Assembly Name");
            Console.WriteLine(assemA.FullName);
            Console.WriteLine(assemB.FullName);
Fully Qualified Assembly Name
System.Console, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
ConsoleApp4, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

There is a PublicKeyToken property. As we introduced the method to get assemblies before, using PublicKeyToken, we can also use Load to load assemblies.

However, you can see from the output above that System.Console has a PublicKeyToken value, whereas your created project ConsoleApp4 does not.

2.2.2 AssemblyName

AssemblyName is a type used to fully describe an assembly.

AssemblyName is used to obtain various information about the assembly class and does not have operational functionality; it is only used to acquire metadata information of the assembly.

Instances of AssemblyName can be obtained using the GetName() method of the Assembly.

Property Description
CodeBase Gets or sets the URL location of the assembly.
ContentType Gets or sets a value indicating the content type contained in the assembly.
CultureInfo Gets or sets the regional settings supported by the assembly.
CultureName Gets or sets the regional settings name associated with this assembly.
EscapedCodeBase Gets the URI including escape symbols representing the base code.
Flags Gets or sets the attributes of the assembly.
FullName Gets the full name of the assembly (also known as the display name).
HashAlgorithm Gets or sets the hash algorithm used for the assembly manifest.
KeyPair Gets or sets the public/private key pair used to create the strong name signature for the assembly.
Name Gets or sets the simple name of the assembly. This is usually (but not necessarily) the file name of the assembly's manifest without its extension.
ProcessorArchitecture Gets or sets a value that identifies the processor and bitness of the target platform for the executable file.
Version Gets or sets the major version, minor version, build number, and revision number of the assembly.
VersionCompatibility Gets or sets information related to the compatibility of the assembly with other assemblies.
            AssemblyName assemNameA = assemA.GetName();
            AssemblyName assemNameB = assemB.GetName();
        Console.WriteLine("Assembly Name: {0}", assemNameA.Name);
        Console.WriteLine("Assembly Name: {0}", assemNameB.Name);

        // Version
        Console.WriteLine("\nVersion: {0}.{1}",
            assemNameA.Version.Major, assemNameA.Version.Minor);
        Console.WriteLine("Version: {0}.{1}",
assemNameB.Version.Major, assemNameB.Version.Minor);

        // Physical file location of the assembly
        Console.WriteLine("\nAssembly CodeBase:{0}", assemA.CodeBase);
        Console.WriteLine("\nAssembly CodeBase:{0}", assemB.CodeBase);</code></pre>

Output information:

Assembly Name: System.Console
Assembly Name: ConsoleApp4

Version: 4.1 Version: 1.0

Assembly CodeBase: file:///x:/Program Files/dotnet/shared/Microsoft.NETCore.App/3.0.1/System.Console.dll

Assembly CodeBase: file:///X:/Users/whuanle/source/repos/ConsoleApp4/ConsoleApp4/bin/Debug/netcoreapp3.0/ConsoleApp4.dll

In addition to GetName(), the Assembly class also provides many properties related to assembly information. For example:

  • GetName method returns an AssemblyName object that provides access to various parts of the assembly's display name.
  • GetCustomAttributes method lists the attributes applied to the assembly.
  • GetFiles method provides access to the files in the assembly manifest.
  • GetManifestResourceNames method provides the names of resources in the assembly manifest.
  • 2.3 Ways to Load Assemblies

    As mentioned above, there are generally three methods to load assemblies:

    • Assembly.Load()
    • Assembly.LoadFrom()
    • Assembly.LoadFile()

    The runtime acquisition and LoadFrom methods have been demonstrated above.

    Next, we will continue to introduce Assembly.Load() and Assembly.LoadFile().

    2.3.1 Assembly.Load()

    Assembly.Load() loads an assembly in a strongly-typed manner,

    where strong name and assembly signature refer to the assembly having a unique and immutable identity.

    What constitutes a strong type? It is achieved by adding the following two metadata elements in the manifest:

    • A unique identifier belonging to the author of the assembly;

    • The hash value of the assembly signature, to confirm that the assembly was generated by the author holding its unique identifier;

    For more information on this topic, you can refer to section "18.2 Strong Names and Assembly Signatures" in "C# 7.0 Core Technical Guide," and it will not be elaborated here.

    Assembly.Load() loads the assembly while also automatically loading other assemblies that the loaded assembly references, without causing any duplicate loading issues.

    Example of usage:

                Assembly assemA = Assembly.Load("System.Console");
                Assembly assemB = Assembly.Load("ConsoleApp4");
                Assembly assemC = Assembly.Load("System.Console, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");

    Reference: Detailed Explanation of Assembly.Load (c#)

    Address: https://www.cnblogs.com/weifeng123/p/8855629.html

    Reference: In-depth Understanding of Assembly.Load(), Assembly.LoadFrom(), and Assembly.LoadFile() Methods in C# Reflection

    Address: https://blog.csdn.net/xuchen_wang/article/details/92773260

    2.3.2 Assembly.LoadFile()

    Assembly.LoadFile() is used in the same way as Assembly.LoadFrom.

    Difference: Assembly.LoadFile() will only load a specified single assembly; Assembly.LoadFrom will load one assembly and then automatically load other assemblies that this assembly depends on.

痴者工良

高级程序员劝退师

文章评论