C# 反射

反射是一种在运行时动态获取程序类型信息的技术,它可以用来查找和操作程序中的类型、成员、属性和方法等。

(1)获取Type类型的几种方法:
(a) 实例调用GetType(b)typeof(类型) (c)Assembly.GetType(类型名称) (d) Type.GetType(类型全称)
(2)获取数组类型
typeof(类型).MakeArrayType()
typeof(int).MakeArrayType()==typeof(int[]) //为true
(3)根据数组类型返回元素类型
typeof(int[]).GetElementType()==typeof(int)//为true
(4)类型具有Namespace,Name,FullName属性,FullName基本等于前两者组合在一起。
(5)数组,指针,ref,out 参数类型名称

 Console.WriteLine(typeof(int).MakePointerType().FullName);
            Console.WriteLine(typeof(bool).MakeByRefType().FullName);
            Console.WriteLine(typeof(bool).MakeArrayType(3).FullName);
System.Int32*
System.Boolean&
System.Boolean[,,]

(6)泛型类型名称

Console.WriteLine(typeof(Dictionary<,>).Name);
            Console.WriteLine(typeof(Dictionary<string,int>).FullName);
Dictionary`2
System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]

MakeGenericType的使用:
MakeGenericType 方法用于创建一个泛型类型的实例,其中可以通过传递类型参数来指定具体的泛型参数类型。这在需要在运行时动态创建泛型类型的情况下非常有用。下面是一个示例代码演示如何使用 MakeGenericType 方法:

假设有一个泛型类 MyGenericClass,你想要在运行时为其指定具体的类型参数并创建实例。首先,定义泛型类如下:

using System;

public class MyGenericClass<T>
{
    public void PrintType()
    {
        Console.WriteLine(typeof(T).Name);
    }
}

接下来,可以使用 MakeGenericType 方法来动态创建泛型类型的实例:

using System;

class Program
{
    static void Main(string[] args)
    {
        // 获取泛型类型的定义
        Type genericTypeDefinition = typeof(MyGenericClass<>);

        // 指定泛型类型参数
        Type[] typeArguments = { typeof(int) };

        // 使用MakeGenericType创建泛型类型实例
        Type specificType = genericTypeDefinition.MakeGenericType(typeArguments);
        object instance = Activator.CreateInstance(specificType);

        // 调用泛型类型的方法
        var printMethod = specificType.GetMethod("PrintType");
        printMethod.Invoke(instance, null);
    }
}


在这个示例中,首先获取了泛型类型的定义 MyGenericClass<>,然后指定了具体的泛型类型参数,例如 int。接着使用 MakeGenericType 创建了指定参数的泛型类型实例,并通过 Activator.CreateInstance 创建了实例对象。最后,使用反射调用了泛型类型的方法。

这个示例展示了如何在运行时动态创建并使用泛型类型的实例。请注意,在实际应用中,可能需要添加错误处理和适当的参数验证。

(7)基本类型和接口

Type base1 = typeof(string).BaseType;
            Type base2 = typeof(FileStream).BaseType;
            Console.WriteLine(base1.Name+"/"+base2.Name);
Object/Stream

GetInterfaces()返回类型实现的所有接口:

foreach(Type t in typeof(string).GetInterfaces())
            {
                Console.WriteLine(t.Name);
            }
IComparable
IEnumerable
IConvertible
IEnumerable`1
IComparable`1
IEquatable`1
ICloneable

IsInstanceOfTypeIsAssignableFrom 都是 C# 中用于类型检查和类型转换的方法。它们用于判断一个对象或一个类型是否与另一个类型相关或相符。

IsInstanceOfType 方法:
IsInstanceOfType 方法用于检查一个对象是否是指定类型的实例。该方法接受一个对象作为参数,然后判断该对象是否是给定类型的实例或派生类的实例。如果是,返回 true,否则返回 false。

Type type = typeof(string);
object obj = "Hello, world!";

bool isInstance = type.IsInstanceOfType(obj);
Console.WriteLine(isInstance);  // 输出:True

Type type = typeof(string);
object obj = "Hello, world!";

bool isInstance = type.IsInstanceOfType(obj);
Console.WriteLine(isInstance);  // 输出:True


IsAssignableFrom 方法:
IsAssignableFrom 方法用于检查一个类型是否可以从另一个类型进行赋值。也就是说,它判断一个类型是否可以作为另一个类型的实例或派生类的实例。如果一个类型可以被赋值给另一个类型,那么后者可以作为前者的基类。

Type baseType = typeof(object);
Type derivedType = typeof(string);

bool isAssignable = baseType.IsAssignableFrom(derivedType);
Console.WriteLine(isAssignable);  // 输出:True


示例演示:

下面是一个示例,演示了如何使用 IsInstanceOfType 和 IsAssignableFrom 进行类型检查和转换:

using System;

class Program
{
    static void Main()
    {
        // IsInstanceOfType 示例
        Type targetType = typeof(int);
        object value = 42;

        bool isInstanceOf = targetType.IsInstanceOfType(value);
        Console.WriteLine($"IsInstanceOfType: {isInstanceOf}");

        // IsAssignableFrom 示例
        Type baseType = typeof(Number);
        Type derivedType = typeof(Integer);

        bool isAssignable = baseType.IsAssignableFrom(derivedType);
        Console.WriteLine($"IsAssignableFrom: {isAssignable}");
        
        Console.WriteLine(typeof(IJohnYang).IsAssignableFrom(typeof(JohnYang)));//True
            Console.WriteLine(typeof(JohnYang).BaseType==typeof(IJohnYang));//False


    }
}

class Number { }
class Integer : Number { }



在这个示例中,我们首先使用 IsInstanceOfType 检查一个对象是否是某个类型的实例,然后使用 IsAssignableFrom 检查一个类型是否可以赋值给另一个类型。

BaseType 用于获取一个类型的最直接的基类,但对于接口来说,它返回 null。
IsAssignableFrom 用于检查一个类型是否可以赋值给另一个类型,包括检查类是否实现了接口。对于接口来说,它通常用于检查一个类是否实现了某个接口。

class A { }
        class B : A { }
        class C : B { }
static void Main(string[] args)
        {
            bool result = typeof(A).IsAssignableFrom(typeof(C));//True
            var resultA = (typeof(C).BaseType == typeof(A));//False
}

(8)实例化类型
(a)调用静态的Activator.CreateInstance
(b)调用ConstructorInfo实例的Invoke方法,此种情形适用于,(a)中所用参数值,无法甄别重载构造器。
Activator.CreateInstance 和 ConstructorInfo.Invoke 都用于创建类的实例,但它们在用法和适用场景上有所不同。
(a) 调用静态的 Activator.CreateInstance:
Activator.CreateInstance 是一个静态方法,它可以用于创建指定类型的实例。它允许你传递构造函数参数来初始化实例。这个方法非常方便,因为它可以根据参数的数量和类型来选择正确的构造函数进行实例化。

Type targetType = typeof(MyClass);
object[] constructorArgs = new object[] { "parameter1", 42 };

object instance = Activator.CreateInstance(targetType, constructorArgs);


注意,Activator.CreateInstance 能够智能地选择合适的构造函数,即使存在多个重载的构造函数也可以适当匹配。

(b) 调用 ConstructorInfo 实例的 Invoke 方法:
ConstructorInfo.Invoke 方法是用于通过反射创建实例的一种方式。你需要先获取目标类型的 ConstructorInfo 实例,然后调用其 Invoke 方法来创建实例。

Type targetType = typeof(MyClass);
ConstructorInfo constructor = targetType.GetConstructor(new Type[] { typeof(string), typeof(int) });
object[] constructorArgs = new object[] { "parameter1", 42 };

object instance = constructor.Invoke(constructorArgs);

在这种情况下,你需要明确指定要使用的构造函数,因为你通过 GetConstructor 方法选择了一个特定的构造函数。这在存在多个重载构造函数时是非常有用的。

比较:
Activator.CreateInstance 是一个更简便的方法,它会根据传递的参数自动匹配适当的构造函数,适用于多个重载构造函数的情况。
ConstructorInfo.Invoke 方法需要你明确指定要使用的构造函数,适用于需要精确控制实例化过程的情况。
请注意,在大多数情况下,推荐使用 Activator.CreateInstance,因为它更简单且适用于大多数常见情况。而 ConstructorInfo.Invoke 主要在需要更精细的构造函数选择时才会使用。

Delegate.CreateDelegate可以动态实例化委托对象:

internal class Program
    {
        delegate int IntFunc(int x);
        static int Square(int x) { return x * x; }
        int Cube(int x) { return x * x * x; }
        static void Main(string[] args)
        {
            Delegate staticD = Delegate.CreateDelegate(typeof(IntFunc), typeof(Program), "Square");//静态方法
            Delegate instanceD = Delegate.CreateDelegate(typeof(IntFunc), new Program(), "Cube");//实例方法
            Console.WriteLine(staticD.DynamicInvoke(3));
            Console.WriteLine(instanceD.DynamicInvoke(3));
        }
  }

9
27

Delegate.CreateDelegate(type,MethodInfo)也常用这样使用委托提高性能。

(9) 泛型类型

 Type closed = typeof(List<int>);
            List<int> list = (List<int>)Activator.CreateInstance(closed);

只能实例化bound泛型类型。

Type unbound=typeof(List<>);//未绑定的泛型类型
Type closed=unbound.MakeGenericType(typeof(int));//通过`MakeGenericType`来将未绑定的泛型类型转变为绑定的泛型类型

Type unbound2=closed.GetGenericTypeDefinition();//通过`GetGenericTypeDefinition`来将绑定的泛型类型转变为非绑定的泛型类型

(10)DeclaringType和ReflectedType
前者返回定义该成员的类,后者返回调用该方法的类型。

 MethodInfo test = typeof(Program).GetMethod("ToString");
            MethodInfo obj = typeof(Object).GetMethod("ToString");
            Console.WriteLine(test.DeclaringType.Name);
            Console.WriteLine(obj.DeclaringType.Name);
            Console.WriteLine(test.ReflectedType.Name);
            Console.WriteLine(obj.ReflectedType.Name);
            Console.WriteLine(test==obj);
            Console.WriteLine(test.MethodHandle==obj.MethodHandle);
Object
Object
Program
Object
False
True

(11)动态调用成员

var a = typeof(DotReinBarM).GetField("DrawInterval", BindingFlags.Static | BindingFlags.Public).GetValue(null);

例如,上面例子,得到DotReinBarM类的静态公开字段DrawInterval的值,注意,GetValue第一个值为实例,若为静态成员,则为null,第二位是访问索引器,所必须的索引

object s = "Hello";
            PropertyInfo prop = s.GetType().GetProperty("Length");
            Console.WriteLine(prop.GetValue(s));
5
Type type = typeof(string);
            Type[] paramTypes = { typeof(int) };
            MethodInfo method = type.GetMethod("Substring", paramTypes);//传入Type[],以确认相应的方法
            object[] arguments = { 2 };
            object returnValue = method.Invoke("Stamp",arguments );//Invoke调用方法,并传入参数
            Console.WriteLine(returnValue);
amp

对ref out的处理:

Type type = typeof(double);
            Type[] paramTypes = { typeof(string), typeof(double).MakeByRefType() };
            MethodInfo method = type.GetMethod("TryParse", paramTypes);
            object[] arguments = { "23.21", 0d };
            bool result=(bool)method.Invoke(null, arguments);//静态方法,所以第一个参数是null
            Console.WriteLine(result.ToString() + ":" + arguments[1]);
True:23.21

(12)访问非公有成员
使用反射访问非公有成员的功能非常强大,但是也非常危险!因为这样可以绕过封装层,并在类型的内部实现中创建无法管理的依赖
注意,必须指定BindingFlags!!

class WalNut
    {
        private bool cracked;
        public void Crack() { cracked = true; }
        public override string ToString()
        {
            return cracked.ToString();
        }
    }

class Program
{

  public static void Main()
  {
Type t = typeof(WalNut);
            WalNut w = new WalNut();
            w.Crack();
            Console.WriteLine(w);

            //开始侵入
            FieldInfo f = t.GetField("cracked", BindingFlags.NonPublic | BindingFlags.Instance);
            f.SetValue(w, false);
            Console.WriteLine(w);
  }
}
True
False


(13) 泛型方法的调用
无法直接调用,需要一个额外的步骤:**即在MethodInfo对象上调用MakeGenericMethod**来指定确切的泛型类型参数

MethodInfo method = typeof(Program).GetMethod("Echo");
            Console.WriteLine(method.IsGenericMethod);
            Console.WriteLine(method.IsGenericMethodDefinition);
            Console.WriteLine("------------------");
            method = method.MakeGenericMethod(typeof(double));
            Console.WriteLine(method.IsGenericMethod);
            Console.WriteLine(method.IsGenericMethodDefinition);
            Console.WriteLine(method.Invoke(null,new object[] {3.21d}));
True
True
------------------
True
False
3.21

(14)GetNestedTypes
Type.GetNestedTypes 是一个用于获取指定类型内嵌(嵌套)的所有类型的方法。嵌套类型是在一个类、结构体、接口或枚举内部声明的类型。GetNestedTypes 方法允许你检索包含在给定类型内部的所有这些嵌套类型。它返回一个 Type[] 数组,其中包含了指定类型中的所有嵌套类型。

以下是 Type.GetNestedTypes 方法的详细用法示例:

using System;

namespace NestedTypesDemo
{
    public class OuterClass
    {
        public class NestedClassA
        {
            public void MethodA() { }
        }

        public class NestedClassB
        {
            public void MethodB() { }
        }

        internal enum NestedEnum
        {
            Value1,
            Value2
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Type outerType = typeof(OuterClass);

            // 获取嵌套类型数组
            Type[] nestedTypes = outerType.GetNestedTypes();

            Console.WriteLine($"嵌套类型数量: {nestedTypes.Length}");

            foreach (Type nestedType in nestedTypes)
            {
                Console.WriteLine($"嵌套类型名称: {nestedType.Name}");
            }
        }
    }
}


在这个示例中,我们定义了一个名为 OuterClass 的外部类,其中包含两个嵌套类 NestedClassA 和 NestedClassB,还有一个嵌套枚举 NestedEnum。然后,在 Main 方法中,我们使用 typeof(OuterClass) 获取了 OuterClass 类型,然后调用了 GetNestedTypes 方法来获取这个类型的所有嵌套类型。

输出结果将会是:

嵌套类型数量: 3
嵌套类型名称: NestedClassA
嵌套类型名称: NestedClassB
嵌套类型名称: NestedEnum

·以下是一个简单的利用反射查找、创建对象、调用方法、获取/修改属性、字段的示例代码:

using System;
using System.Reflection;

class Program
{
    static void Main(string[] args)
    {
        // 通过反射查找类型
        Type type = Type.GetType("Demo.Person");

        // 通过反射创建对象
        object person = Activator.CreateInstance(type);

        // 通过反射调用方法
        MethodInfo methodInfo = type.GetMethod("SayHello");
        methodInfo.Invoke(person, null);

        // 通过反射获取属性
        PropertyInfo propertyInfo = type.GetProperty("Name");
        Console.WriteLine("Name: {0}", propertyInfo.GetValue(person));

        // 通过反射修改属性
        propertyInfo.SetValue(person, "Tom", null);
        Console.WriteLine("Name: {0}", propertyInfo.GetValue(person));

        // 通过反射获取字段
        FieldInfo fieldInfo = type.GetField("Age");
        Console.WriteLine("Age: {0}", fieldInfo.GetValue(person));

        // 通过反射修改字段
        fieldInfo.SetValue(person, 20);
        Console.WriteLine("Age: {0}", fieldInfo.GetValue(person));
    }
}

class Person
{
    public string Name { get; set; }
    public int Age;

    public void SayHello()
    {
        Console.WriteLine("Hello, my name is {0}.", Name);
    }
}


在这个示例中,我们通过反射查找了一个名为Demo.Person的类型,并创建了一个该类型的对象。然后,我们使用反射获取了该对象的SayHello方法,并通过Invoke方法调用了该方法。接着,我们使用反射获取了该对象的Name属性,并获取了该属性的值。然后,我们通过反射修改了该对象的Name属性的值,并再次获取了该属性的值。最后,我们使用反射获取了该对象的Age字段,并获取了该字段的值。然后,我们通过反射修改了该对象的Age字段的值,并再次获取了该字段的值。

·使用反射调用构造器,可以通过以下步骤实现:

通过Type.GetType方法或者typeof关键字获取目标类型的Type对象。例如,获取Demo.Person类型的Type对象可以使用以下代码:Type type = Type.GetType("Demo.Person"); 或者 Type type = typeof(Demo.Person);

使用Type.GetConstructor方法获取目标类型的构造器。该方法接受一个Type[]参数,用于指定构造器的参数类型。如果要调用无参数构造器,可以使用Type.GetConstructor(Type.EmptyTypes)方法。例如,获取Demo.Person类型的无参数构造器可以使用以下代码:ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);

使用ConstructorInfo.Invoke方法调用构造器。该方法接受一个object[]参数,用于指定构造器的参数值。如果调用无参数构造器,该参数可以传入null或者空数组。例如,调用Demo.Person类型的无参数构造器可以使用以下代码:object person = constructor.Invoke(null);
完整示例代码如下:

using System;
using System.Reflection;

class Program
{
    static void Main(string[] args)
    {
        // 获取类型
        Type type = typeof(Demo.Person);

        // 获取构造器
        ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);

        // 调用构造器
        object person = constructor.Invoke(null);

        // 调用对象方法
        MethodInfo methodInfo = type.GetMethod("SayHello");
        methodInfo.Invoke(person, null);
    }
}

class Person
{
    public void SayHello()
    {
        Console.WriteLine("Hello, I'm a new person.");
    }
}

在这个示例中,我们使用Type.GetConstructor(new Type[] { typeof(string), typeof(int) })获取了Demo.Person类型的接受string和int类型参数的构造器,并使用ConstructorInfo.Invoke(new object[] { "Tom", 20 })方法调用该构造器,创建了一个带有Name和Age属性的Demo.Person类型的对象。

·Activator.CreateInstance和constructor.Invoke都可以用于创建对象,但它们的实现方式有所不同。

Activator.CreateInstance是一个静态方法,它使用指定的类型名、程序集名、参数等信息来创建一个实例。它可以自动选择适当的构造函数进行创建,并且支持泛型类型的创建。使用Activator.CreateInstance可以避免手动获取构造函数的过程,让创建对象的过程更加简便。但是,由于其通过字符串来指定类型名和程序集名,因此需要在编译时指定完整的类型名和程序集名,不太方便动态获取类型。

constructor.Invoke则是使用反射获取到一个构造函数后,通过Invoke方法来调用构造函数,创建一个对象。与Activator.CreateInstance相比,使用constructor.Invoke需要手动获取构造函数,需要明确指定构造函数的参数,因此相对来说更加复杂。但是,它可以在运行时动态获取类型和构造函数,更加灵活。

总的来说,Activator.CreateInstance适用于已知类型名和程序集名的情况,可以让创建对象更加简便;而constructor.Invoke适用于需要动态获取类型和构造函数的情况,更加灵活。

ReflectionHelper.cs


using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.PortableExecutable;

namespace John.Commons
{
    public static class ReflectionHelper
    {
        private class AssemblyEquality : EqualityComparer<Assembly>
        {
            public override bool Equals(Assembly? x, Assembly? y)
            {
                if (x == null && y == null)
                {
                    return true;
                }

                if (x == null || y == null)
                {
                    return false;
                }

                return AssemblyName.ReferenceMatchesDefinition(x!.GetName(), y!.GetName());
            }

            public override int GetHashCode([DisallowNull] Assembly obj)
            {
                return obj.GetName().FullName.GetHashCode();
            }
        }

        private static bool IsSystemAssembly(Assembly asm)
        {
            return asm.GetCustomAttribute<AssemblyCompanyAttribute>()?.Company.Contains("Microsoft") ?? false;
        }

        private static bool IsManagedAssembly(string file)
        {
            using FileStream peStream = File.OpenRead(file);
            using PEReader pEReader = new PEReader((Stream)peStream);
            return pEReader.PEHeaders.CorHeader != null;
        }

        public static IEnumerable<Assembly> GetAllReferencedAssemblies(bool skipSystemAssemblies = true)
        {
            Assembly assembly = Assembly.GetEntryAssembly();
            if (assembly == null)
            {
                assembly = Assembly.GetCallingAssembly();
            }

            HashSet<Assembly> hashSet = new HashSet<Assembly>(new AssemblyEquality());
            HashSet<string> hashSet2 = new HashSet<string>();
            Queue<Assembly> queue = new Queue<Assembly>();
            queue.Enqueue(assembly);
            if (skipSystemAssemblies && IsSystemAssembly(assembly))
            {
                hashSet.Add(assembly);
            }

            while (queue.Any())
            {
                AssemblyName[] referencedAssemblies = queue.Dequeue().GetReferencedAssemblies();
                foreach (AssemblyName assemblyName in referencedAssemblies)
                {
                    if (!hashSet2.Contains(assemblyName.FullName))
                    {
                        Assembly assembly2 = Assembly.Load(assemblyName);
                        if (!skipSystemAssemblies || !IsSystemAssembly(assembly2))
                        {
                            queue.Enqueue(assembly2);
                            hashSet2.Add(assemblyName.FullName);
                            hashSet.Add(assembly2);
                        }
                    }
                }
            }

            foreach (string item in Directory.EnumerateFiles(AppContext.BaseDirectory, "*.dll", new EnumerationOptions
            {
                RecurseSubdirectories = true
            }))
            {
                if (!IsManagedAssembly(item))
                {
                    continue;
                }

                AssemblyName asmName = AssemblyName.GetAssemblyName(item);
                if (!hashSet.Any((Assembly x) => AssemblyName.ReferenceMatchesDefinition(x.GetName(), asmName)))
                {
                    Assembly assembly3 = Assembly.Load(asmName);
                    if (!skipSystemAssemblies || !IsSystemAssembly(assembly3))
                    {
                        hashSet.Add(assembly3);
                    }
                }
            }

            return hashSet.ToArray();
        }
    }
}

posted @ 2023-03-11 11:19  JohnYang819  阅读(162)  评论(0编辑  收藏  举报