- 1. Overview of Reflection Usage
- 2. Obtaining Type
In the previous chapter, we explored various methods for importing assemblies in C#. In this chapter, the author will explore various operations and code practices for using reflection in C#.
1. Overview of Reflection Usage
1.1 What is Reflection
According to C# 7.0 in a Nutshell:
Reflection refers to the process of examining metadata in an assembly.
According to C# 7.0 Core Technical Guide:
The operation of inspecting and using metadata and compiled code at runtime is called reflection.
According to Microsoft Docs:
Reflection provides objects that describe assemblies, modules, and types. It allows for dynamically creating instances of types, binding types to existing objects, or retrieving types from existing objects, then calling their methods or accessing their fields and properties.
1.2 What can Reflection do
From C# 7.0 in a Nutshell, C# 7.0 Core Technical Guide, and Microsoft Docs, the roles of reflection can be summarized as follows:
- When you need to access attributes in assembly metadata;
- To inspect and instantiate types in an assembly;
- To build new types at runtime (using Emit technology);
- To perform late binding to access methods on types created at runtime;
- To access metadata of types in an assembly:
This includes constructs like fully qualified type names and member names, along with any attributes that modify a construct. Use metadata to dynamically invoke members of a type at runtime rather than using compile-time binding. - Many services provided by .NET through C# (such as dynamic binding, serialization, data binding, and remoting) rely on metadata:
Our applications can make full use of this metadata and even add information to it through custom attributes. We can even dynamically create new metadata and executable IL (Intermediate Language) instructions at runtime using classes in the System.Reflection.Emit namespace.
1.3 Type Class
System.Type
Represents type declarations: class types, interface types, array types, value types, enumeration types, type parameters, generic type definitions, as well as generic types that are open or closed.
The Type type is the foundation of reflection techniques; all operations involving reflection relate to Type.
1.4 Categories of Reflection Usage
In C#, a type may consist of the following elements:
Type name, constructor/parameters, methods/method parameters, fields, properties, base type, inherited interfaces, etc.
When using reflection techniques, we generally focus on the following information:
-
Type's name
Type.Name
-
Whether the type is public
Type.IsPublic
-
Type's base type
Type.BaseType
-
Which interfaces the type supports
Type.GetInterfaces()
-
Which assembly defines the type
Type.Assembly
-
Type's properties, methods, fields
Type.GetProperties() Type.GetMethods() Type.GetFields()
-
Attributes that modify the type
Type.GetCustomAttributes()
1.4 Type Some Common Properties
Type type = typeof(Program);
Console.WriteLine(type.Namespace);
Console.WriteLine(type.Name); // Type name
Console.WriteLine(type.FullName); // Fully qualified name of the type
Assembly ass = type.Assembly;
Console.WriteLine(ass.FullName);</code></pre>
Output:
Mytest
Program
Mytest.Program
ConsoleApp4, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Next, the author will gradually explore the content of reflection in C# and verify through practice.
2. Obtaining Type
2.1 Getting Type
There are mainly two methods to obtain the Type:
Type type1 = typeof(MyClass);
MyClass myClass = new MyClass();
Type type2 = myClass.GetType();</code></pre>
typeof()
static method directly retrieves a type's Type;
.GetType()
retrieves the type of an instance;
These two methods should be quite familiar to everyone~
Reflection is generally used when the type cannot be clearly determined while writing code, often in combination with assemblies:
Assembly ass = Assembly.LoadFrom(@"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.0.0\ref\netcoreapp3.0\System.Console.dll");
// Get the assembly of the current console program and get the Console type
// Note, use fully qualified name
Type type = ass.GetType("System.Console");
Type[] types = ass.GetTypes();
Console.WriteLine("type: " + type.Name);
// Get all types in System.Console.dll
foreach (var item in types)
{
Console.WriteLine("item: " + item.Name);
}
Console.ReadKey();</code></pre>
For fully qualified names, fill in as per the actual situation.
2.2 Array Type
To get the Type of an array, there are two situations: one is generating an array type from a type, and the other is the type itself being an array type;
For example,
It is an int type, generating the Type of int[] array;
It is an int[] type, generating the Type of int[] array;
Generating Array Type
The former generates using the instance's MakeArrayType()
method, as shown below:
// int generates int[]
Type typeArray_A = typeof(int).MakeArrayType();
// int generates int[,] multi-dimensional array
Type typeArray_B = typeof(int).MakeArrayType(2);
Console.WriteLine(typeArray_A.Name);
Console.WriteLine(typeArray_B.Name);
Console.ReadKey();</code></pre>
Output:
Int32[]
Int32[,]
If it's a jagged array [][]
... how to create it... don't worry... it will be covered later...
Obtaining Array Type
What if a type itself is an array?
Type type = typeof(int[]);
Console.WriteLine(type);
No operation is needed...
Getting Element Type and Dimension of Array
If there's an array int[]
, we want to get the element type int, using:
Type type = typeof(int[]);
Type type1 = type.GetElementType();
Console.WriteLine(type1.Name);
To get the dimension of a multi-dimensional array:
Type type = typeof(int[,,,,,,,,,,]);
Console.WriteLine(type.GetArrayRank());
Output: 11
Type type = typeof(int[][][][][]);
Console.WriteLine(type.GetArrayRank());
Output: 1
Rectangular Arrays (Jagged Arrays)
int[,]
is known as a multi-dimensional array;
While int[][]
is referred to as a rectangular array, jagged array, or serrated array (there are many names).
For more knowledge on this aspect, refer to the author's other article: https://www.cnblogs.com/whuanle/p/9936047.html
Type does not provide a method to create jagged arrays, because in reality, a jagged array is an array of arrays, such as an array of (int[])
.
// First, get a type
Type arrayType = typeof(int[]);
Type _Array = arrayType.MakeArrayType();
Console.WriteLine(_Array.Name);
Output:
Int32[][]
2.3 Nested Types
The usage of nested types is the same as that of normal types. The fully qualified name of a nested type consists of OuterType+NestedType
, and there are no other differences elsewhere.
In Program, create a class MyClass
.
Type type = typeof(Program.MyClass);
Console.WriteLine(type.Namespace);
Console.WriteLine(type.Name);
Console.WriteLine(type.FullName);
Assembly ass = type.Assembly;
Console.WriteLine(ass.FullName);</code></pre>
Output:
Mytest
MyClass
Mytest.Program+MyClass
ConsoleApp4, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
The fully qualified name: Mytest.Program+MyClass
, connected by a plus sign +
.
2.4 Generic Type
Generic Information
Look at the following example:
Type typeA = typeof(Dictionary<,>);
Type typeB = typeof(Dictionary);
Type typeC = typeof(List<>);
Type typeD = typeof(List);
Then print Name
:
Console.WriteLine(typeA.Name);
Console.WriteLine(typeB.Name);
Console.WriteLine(typeC.Name);
Console.WriteLine(typeD.Name);
Output:
Dictionary`2
Dictionary`2
List`1
List`1
Print FullName
:
Console.WriteLine(typeA.FullName);
Console.WriteLine(typeB.FullName);
Console.WriteLine(typeC.FullName);
Console.WriteLine(typeD.FullName);
Output:
System.Collections.Generic.Dictionary`2
System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
System.Collections.Generic.List`1
System.Collections.Generic.List`1[[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
Dictionary`2
, the ` after indicates how many generic parameters this type has.
The .Name
cannot reveal the parameters and their types; FullName
shows the complete parameters.
Generic Related
In Type, the following functions are related to generics:
System.Type Member Name
Description
IsGenericType
Returns true if the type is generic.
GetGenericArguments()
Returns an array of Type
objects representing the type arguments provided to a constructed type or the type parameters of a generic type definition.
GetGenericTypeDefinition()
Returns the underlying generic type definition of the current constructed type.
GetGenericParameterConstraints()
Returns an array of Type
objects representing constraints on the current generic type parameter.
ContainsGenericParameters()
Returns true if the type or any of its enclosing types or methods contain type parameters for which specific types have not been provided.
GenericParameterAttributes()
Gets a combination of GenericParameterAttributes
flags that describe special constraints on the current generic type parameter.
GenericParameterPosition()
For a Type
object representing a type parameter, gets the position of the type parameter in the list of type parameters of the generic type definition or generic method definition that declares it.
IsGenericParameter
Gets a value indicating whether the current Type
represents a type parameter in a generic type or method definition.
IsGenericTypeDefinition
Gets a value indicating whether the current Type represents a generic type definition that can be used to construct other generic types. Returns true if the type represents the definition of a generic type.
DeclaringMethod()
Returns the generic method that defines the current generic type parameter, or null if the type parameter is not defined by a generic method.
MakeGenericType()
Replaces the elements of a type array consisting of type parameters defined by the current generic type, and returns a Type object that represents the result constructed type.
Type typeA = typeof(Dictionary<,>);
Type typeB = typeof(Dictionary);
Console.WriteLine(typeA.IsGenericMethodParameter +"|"+typeB.IsGenericMethodParameter);
Console.WriteLine(typeA.IsGenericParameter + "|" + typeB.IsGenericParameter);
Console.WriteLine(typeA.IsGenericType + "|" + typeB.IsGenericType);
Console.WriteLine(typeA.IsGenericTypeDefinition + "|" + typeB.IsGenericTypeDefinition);
Console.WriteLine(typeA.IsGenericTypeParameter + "|" + typeB.IsGenericTypeParameter);
Output:
False|False
False|False
True|True
True|False
False|False
To be honest, besides IsGenericType
, I don’t understand what the others mean.
GetGenericArguments()
can be used to obtain the parameter types of a generic.
Type type = typeof(Dictionary);
Type[] list = type.GetGenericArguments();
foreach (var item in list)
Console.WriteLine(item.Name);
Output:
String
Int32
2.5 Method Parameters and ref / out
Creating a type:
public class MyClass
{
public void Test(string str, ref string a, out string b)
{
b = "666";
Console.WriteLine(b);
}
}
Getting the parameter list of a method:
// Getting the parameter list of a method
ParameterInfo[] paramList = typeof(MyClass).GetMethod(nameof(MyClass.Test)).GetParameters();
foreach (var item in paramList)
Console.WriteLine(item);
Output:
System.String str
System.String& a
System.String& b
If a parameter type is followed by &, it indicates that it is a ref or out parameter.
文章评论