SoFunction
Updated on 2025-03-07

What you need to know about C# Reflection

Generally, reflection is used to dynamically obtain information such as types, properties, and methods of objects. Today I will take you to play with reflexes to summarize the various common operations of reflexes and find out if there are any that you don’t know.

Get a member of the type

The GetMembers method of the Type class is used to obtain all members of the type, including methods and properties, and can be filtered through the BindingFlags flag.

using System;
using ;
using ;

public class Program
{
  public static voidMain()
  {
    var members = typeof(object).GetMembers( |
       | );
    foreach (var member in members)
    {
      ($"{} is a {}");
    }
  }
}

Output:

GetType is a Method
GetHashCode is a Method
ToString is a Method
Equals is a Method
ReferenceEquals is a Method
.ctor is a Constructor

The GetMembers method can also not pass BindingFlags, and the default returns all public members.

Get and call the method of the object

The GetMethod method of type Type is used to obtain the MethodInfo of this type, and then the method can be called dynamically through MethodInfo.

For non-static methods, the corresponding instance needs to be passed as parameter, example:

class Program
{
  public static void Main()
  {
    var str = "hello";
    var method = ()
      .GetMethod("Substring", new[] {typeof(int), typeof(int)});
    var result = (str, new object[] {0, 4}); // Equivalent to (0, 4)    (result); // Output: hell  }
}

For static methods, the object parameters are passed empty, example:

var method = typeof(Math).GetMethod("Exp");
// Equivalent to (2)var result = (null, new object[] {2});
(result); // Output(e^2):7.38905609893065

If it is a generic method, you also need to create a generic method through generic parameters. Example:

class Program
{
  public static void Main()
  {
    // Reflection calls generic methods    MethodInfo method1 = typeof(Sample).GetMethod("GenericMethod");
    MethodInfo generic1 = (typeof(string));
    (sample, null);

    // Reflection calls static generic methods    MethodInfo method2 = typeof(Sample).GetMethod("StaticMethod");
    MethodInfo generic2 = (typeof(string));
    (null, null);
  }
}

public class Sample
{
  public void GenericMethod<T>()
  {
    //...
  }
  public static void StaticMethod<T>()
  {
    //...
  }
}

Create an instance of type

There are many ways to dynamically create an instance of a type using reflection. The easiest one is to usenew() Declaration of conditions.

Use new conditional statement

If you need to create an instance dynamically within a method, you can directly use the new condition declaration, for example:

T GetInstance<T>() where T : new()
{
  T instance = newT();
  return instance;
}

However, this method has limited application scenarios, such as not applicable to types with constructors with parameters.

Using the Activator class

It is the most common practice to dynamically create an instance of a class using the Activator class, examples:

Type type = typeof(BigInteger);
object result = (type);
(result); // Output: 0result = (type, 123);
(result); // Output:123

Dynamically create a general type instance, you need to create an open generic first (such asList<>), and then convert it to concrete generics according to the generic parameters (such asList<string>), example:

// Create an open generic firstType openType = typeof(List&lt;&gt;);
// Create concrete generics againType[] tArgs = { typeof(string) };
Type target = (tArgs);
// Create a generic instance at the endList&lt;string&gt; result = (List&lt;string&gt;)(target);

If you don't know what open and concrete generics are, please see the last section of this article.

Reflection using constructor

You can also create class instances dynamically through reflection constructors, which is slightly more troublesome than using the Activator class above, but the performance is better. Example:

ConstructorInfo c = typeof(T).GetConstructor(new[] { typeof(string) });
if (c == null)
  throw new InvalidOperationException("...");
T instance = (T)(new object[] { "test" });

Use the FormatterServices class

If you want to create an instance of a certain class without performing constructor and attribute initialization, you can use the GetUninitializedObject method of FormatterServices. Example:

class Program
{
  static void Main()
  {
    MyClass instance = (MyClass)(typeof(MyClass));
    (instance.MyProperty1); // Output: 0    (instance.MyProperty2); // Output: 0  }
}

public class MyClass
{
  public MyClass(int val)
  {
    MyProperty1 = val &lt; 1 ? 1 : val;
  }

  public int MyProperty1 { get; }

  public int MyProperty2 { get; set; } = 2;
}

Get a strongly typed delegate for a property or method

After obtaining the properties and methods of the object through reflection, if you want to access or call through strongly typed methods, you can add a layer of delegation in the middle. This benefit is beneficial for encapsulation, and the caller can clearly know what parameters need to be passed during the call. For example, the following method extracts the method as a strongly typed delegation:

var tArgs = new Type[] { typeof(int), typeof(int) };
var maxMethod = typeof(Math).GetMethod("Max", tArgs);
var strongTypeDelegate = (Func&lt;int, int, int&gt;)Delegate
  .CreateDelegate(typeof(Func&lt;int, int, int&gt;), null, maxMethod);
("3 and 5 The biggest between:{0}", strongTypeDelegate(3, 5)); // Output:5

This trick also works with properties, which can get strongly typed Getters and Setters. Example:

var theProperty = typeof(MyClass).GetProperty("MyIntProperty");

// Strong type Gettervar theGetter = ();
var strongTypeGetter = (Func&lt;MyClass, int&gt;)Delegate
  .CreateDelegate(typeof(Func&lt;MyClass, int&gt;), theGetter);
var intVal = strongTypeGetter(target); // Related:
// Strong type Settervar theSetter = ();
var strongTypeSetter = (Action&lt;MyClass, int&gt;)Delegate
  .CreateDelegate(typeof(Action&lt;MyClass, int&gt;), theSetter);
strongTypeSetter(target, 5); // Equivalent to: = 5

Reflect to get custom features

Here are four common scenario examples.

Example 1: Find the attributes marked with a custom attribute (such as MyAtrrite) in a class.

var props = type
  .GetProperties( |  | )
  .Where(prop =>(prop, typeof(MyAttribute)));

Example 2: Find all custom properties of a certain property.

var attributes = typeof(t).GetProperty("Name").GetCustomAttributes(false);

Example 3: Find all classes marked with a custom feature in the assembly.

static IEnumerable<Type> GetTypesWithAttribute(Assembly assembly)
{
  foreach(Type type ())
  {
    if ((typeof(MyAttribute), true).Length > 0)
    {
      yield return type;
    }
  }
}

Example 4: Read the value of a custom feature at runtime

public static class AttributeExtensions
{
  public static TValue GetAttribute&lt;TAttribute, TValue&gt;(
    this Type type,
    string MemberName,
    Func&lt;TAttribute, TValue&gt; valueSelector,
    bool inherit = false)
    where TAttribute : Attribute
  {
    var att = (MemberName).FirstOrDefault()
      .GetCustomAttributes(typeof(TAttribute), inherit)
      .FirstOrDefault() as TAttribute;
    if (att != null)
    {
      return valueSelector(att);
    }
    return default;
  }
}

// use:
class Program
{
  static void Main()
  {
    // Read the value of the Description attribute of the MyMethod method of the MyClass class    var description = typeof(MyClass)
      .GetAttribute("MyMethod", (DescriptionAttribute d) =&gt; );
    (description); // Output: Hello  }
}
public class MyClass
{
  [Description("Hello")]
  public void MyMethod() { }
}

All implementation classes of dynamic instantiation of interfaces (plugin activation)

Dynamically instantiate all implementation classes of an interface through reflection, which are often used to implement plug-in development of the system. For example, when the program starts, you can read the dll file in the specified folder (such as Plugins), and obtain all classes in the dll that implement a certain interface through reflection, and instantiate it when appropriate. The rough implementation is as follows:

interface IPlugin
{
  string Description { get; }
  void DoWork();
}

A class in a standalone dll:

class HelloPlugin : IPlugin
{
  public string Description => "A plugin that says Hello";
  public void DoWork()
  {
    ("Hello");
  }
}

The dll is loaded dynamically when your system starts, reads information about all classes that implement the IPlugin interface, and instantiates it.

public IEnumerable<IPlugin> InstantiatePlugins(string directory)
{
  var assemblyNames = (directory, "*.")
    .Select(name => new FileInfo(name).FullName).ToArray();

  foreach (var fileName assemblyNames)
    ((fileName));

  var assemblies = ();
  var typesInAssembly = (asm =>());
  var pluginTypes = (type => typeof (IPlugin).IsAssignableFrom(type));

  return ().Cast<IPlugin>();
}

Check generic parameters of generic instances

The aforementioned article mentioned constructed generics and concrete generics, so I will explain it here. Most of the time we call generics refer to constructed generics, and sometimes they are also called concrete generics. for exampleList<int>It is a constructed generic because it can be instantiated by new . Correspondingly,List<> Generics are non-constructive generics, sometimes called open generics, and they cannot be instantiated. Open generics can be converted into any concrete generic through reflection, as there is an example in the previous article.

If there is a generic instance now, for some requirement, we want to know what generic parameters are needed to build this generic instance. For example, someone created a List<T> An instance of a generic and pass it as a parameter to a method:

var myList = newList<int>();
ShowGenericArguments(myList);

Our method signature is like this:

public void ShowGenericArguments(object o)

At this time, as the writer of this method, we do not know what type of generic parameters this o object is built with. Through reflection, we can get a lot of information about generic instances, the easiest of which is to determine whether a type is a generic:

public void ShowGenericArguments(object o)
{
  if (o == null) return;
  Type t =();
  if (!) return;
  ...
}

becauseList<> It is also a generic, so the above judgment is not rigorous. What we need to know is whether the object is a constructed generic (List<int>). The Type class also provides some useful properties:

typeof(List<>).IsGenericType // true
typeof(List<>).IsGenericTypeDefinition // true
typeof(List<>).IsConstructedGenericType// false

typeof(List<int>).IsGenericType // true
typeof(List<int>).IsGenericTypeDefinition // false
typeof(List<int>).IsConstructedGenericType// true

IsConstructedGenericType and IsGenericTypeDefinitionUsed to determine whether a generic is a constructed generic and a non-constructed generic.

Combined with Type GetGenericArguments() Methods, you can easily know what generic parameters a generic instance is built with, for example:

static void ShowGenericArguments(object o)
{
  if (o == null) return;
  Type t = ();
  if (!) return;
  foreach (Type genericTypeArgument in ())
    ();
}

The above is a detailed content of the reflection in C#. For more information about C# reflection, please pay attention to my other related articles!