反射、

关于反射

在 C# 中,反射是一种机制,它允许在程序运行时获取类型的信息并操作对象。通过反射,我们可以在运行时动态创建对象、访问对象的属性和方法、获取类型信息等。反射可以帮助我们实现很多功能,如动态创建对象、解析和调用程序集中的方法和属性、动态加载和卸载程序集等。反射是一个强大的机制,但使用不当也可能会对性能产生影响,所以在使用反射时需要慎重考虑。

给一个对象,在不用new操作符的情况下,在不知道具体类型的情况下,创建一个同类型的对象,还能访问这个对象的各个成员。

——↓ 引自《C# 7.0核心技术指南》——————————————————
3.3.3 GetType方法和typeof运算符C#中的所有类型在运行时都会维护System.Type类的实例。有两个基本方法可以获得System.Type对象:
· 在类型实例上调用GetType方法
· 在类型名称上使用typeof运算符
GetType在运行时计算,而typeof在编译时静态计算(如果是用泛型类型参数,那么它将由即时编译器解析)。
System.Type拥有诸多属性,例如类型的名称、程序集、基类型等属性。
例如:

        using System;

        public class Point { public int X, Y; }

        class Test
        {
          static void Main()
          {
            Point p = new Point();
            Console.WriteLine (p.GetType().Name);                // Point
            Console.WriteLine (typeof (Point).Name);             // Point
            Console.WriteLine (p.GetType() == typeof(Point)); // True
            Console.WriteLine (p.X.GetType().Name);              // Int32
            Console.WriteLine (p.Y.GetType().FullName);          // System.Int32
          }
        }

——↑ 引自《C# 7.0核心技术指南》——————————————————


视频学习:【103-反射之Type】 B站:IT萌叔Jack

 //获取值类型的Type类对象
            Type type01=12.GetType();
            Type type02 = typeof(System.Int32);
            Type type03 = Type.GetType("System.Int32");

            //判断三个Type类对象是否相同:True
            Console.WriteLine(type01 == type02);
            Console.WriteLine(type02 == type03);


            //获取引用类型的Type类对象
            Type type04 = new object().GetType();
            Type type05 = typeof(System.Object);
            Type type06 = Type.GetType("System.Object");

            Console.WriteLine(type04 == type05);
            Console.WriteLine(type05 == type06);

视频学习:【104-反射之Field】B站:IT萌叔Jack

  class Person
    {
        //字段
        public string name = "jack";
        public int age = 30;
        public static string nation;
    }

    class Program
    {
        static void Main(string[] args)
        {
            //通过反射获取Person类型信息
            Type type = typeof(反射与依赖注入.Person);
            //获取字段:获取所有public的字段
            // public FieldInfo[ ] GetFields();  返回值是FieldInfo[ ]
            FieldInfo[ ] fieldInfos= type.GetFields();
            //遍历

            for (int i = 0; i < fieldInfos.Length; i++)
            {
                //Console.WriteLine($"字段类型:{fieldInfos[i].FieldType} 字段名称:{fieldInfos[i].Name}  字段值:{fieldInfos[i].GetValue}");
                //非静态字段,需要通过类的对象去访问字段  才能拿到值。 
                Console.WriteLine($"字段类型:{fieldInfos[i].FieldType} 字段名称:{fieldInfos[i].Name}  字段值:{fieldInfos[i].GetValue(new Person() )}");
         
            }

        }
    }


输出:
字段类型:System.String 字段名称:name  字段值:jack
字段类型:System.Int32 字段名称:age  字段值:30
字段类型:System.String 字段名称:nation  字段值:

nation 是引用类型,没有初始化值,默认字段值是null

private int id; 

//想要访问这个私有字段, 可以通过 标记  

 进入 “FieldInfo[ ] fieldInfos= type.GetFields();” 的GetFields方法。 
这是它的一些重载 
  public FieldInfo? GetField(string name);
  public abstract FieldInfo? GetField(string name, BindingFlags bindingAttr);
        public FieldInfo[] GetFields();
        public abstract FieldInfo[] GetFields(BindingFlags bindingAttr);

BindingFlags  

public enum BindingFlags
{
······
Instance = 4,
Static = 8,
NonPublic = 32,

······
}


 FieldInfo[ ] fieldInfos= type.GetFields(BindingFlags.Instance| BindingFlags.NonPublic);
用 | 竖线添加多个 


   class Person
    {
        //字段
        private int id = 1001;
        public string name = "jack";
        public int age = 30;
        public static string nation;
    }

    class Program
    {
        static void Main(string[] args)
        {
            //通过反射获取Person类型信息
            Type type = typeof(反射与依赖注入.Person);
            // BindingFlags.Instance 指定获取的是 Instance 实例字段,   实例字段是非静态的字段 创建之后才能获得的字段
            FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
            // 去获取 public ,static的字段
            FieldInfo[] fieldInfos02 = type.GetFields(BindingFlags.Static | BindingFlags.Public);



            //遍历

            for (int i = 0; i < fieldInfos.Length; i++)
            {
            
                Console.WriteLine($"字段类型:{fieldInfos[i].FieldType} 字段名称:{fieldInfos[i].Name}  字段值:{fieldInfos[i].GetValue(new Person() )}");
            }

            //获取指定的字段
            FieldInfo field = type.GetField("nation");
            //设置值:静态字段 不需要对象, 因此传递null
            field.SetValue(null,"中国");
            //获取值     field.GetValue()  静态的不需要实例,不用传,但是传了也没事,
            //动态的需要实例 需要传 new Person() 
            Console.WriteLine(field.GetValue(null));

        }
    }





简易的访问私有字段和属性:

using System;
using System.Reflection;

public class Example
{
    private int id = 10;

    private void Method()
    {
        Console.WriteLine("私有方法被执行了");
    }

    public static void Main(string[] args)
    {
        var example = new Example();

        // 获取Example对象的类型
        Type type = example.GetType();

        // 获取id字段并设置可访问性
        FieldInfo fieldInfo = type.GetField("id", BindingFlags.NonPublic | BindingFlags.Instance);
        fieldInfo.SetValue(example, 20);

        // 获取Method方法并设置可访问性
        MethodInfo methodInfo = type.GetMethod("Method", BindingFlags.NonPublic | BindingFlags.Instance);
        methodInfo.Invoke(example, null);
    }
}


在这个例子中,我们使用var example = new Example()创建了Example类的实例,并传递给GetType()方法来获取该对象的类型。接着,我们分别通过GetField()和GetMethod()方法获取了需要访问的私有字段和方法的信息,并通过SetValue()和Invoke()方法进行了访问。

需要注意的是,默认情况下,私有字段和方法是不可访问的。因此,我们需要在调用GetField()和GetMethod()方法时,通过参数设置来指定其可访问性。具体来说,在这里我们使用了BindingFlags.NonPublic和BindingFlags.Instance来表示访问非公有成员和实例成员。

另外,C# 还提供了一种dynamic类型,可以在运行时动态解析类型信息。因此,在某些情况下,也可以使用 dynamic来访问私有字段和方法:

using System;

public class Example
{
    private int id = 10;

    private void Method()
    {
        Console.WriteLine("私有方法被执行了");
    }

    public static void Main(string[] args)
    {
        dynamic example = new Example();

        // 访问私有字段
        example.id = 20;
        Console.WriteLine(example.id); // 输出:20

        // 访问私有方法
        example.Method(); // 输出:私有方法被执行了
    }
}




视频学习:【105-反射之Property 属性】B站:IT萌叔Jack


class Person
    {
        //字段
        private int id = 1001;
        private string name = "jack";
        private int age = 30;
        private static string nation;

        //属性 
        public int Id { get => id; set => id = value; }
        public string Name { get => name; set => name = value; }
        public int Age { get => age; set => age = value; }
        public static string Nation { get => nation; set => nation = value; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Type type = new Person().GetType();
            //获取共有的所有属性
         PropertyInfo [ ] propertyInfos= type.GetProperties();
            //遍历
            foreach (PropertyInfo property in propertyInfos)
            {
                Console.WriteLine($"属性类型:{property.PropertyType}  属性名:{property.Name} 属性值:{property.GetValue(new Person())}");
            }

            //获取指定属性
            PropertyInfo info = type.GetProperty("Nation");

            //设置值
            info.SetValue( null,"波兰" );
            // 获取值
            Console.WriteLine(info.GetValue(null));
        }
    }



构造函数里面不能直接使用静态字段, 静态字段要初始化的话,必须要把它都放到静态构造函数中

  public object Invoke(object?[]? parameters);

这是C#中的Constructor.Invoke方法,它用于调用一个对象的构造函数,可以通过传递参数进行初始化。该方法返回一个Object类型的实例,表示新创建的对象。

方法的参数是一个可空的object[]类型,其中包含要传递给构造函数的参数列表。如果该构造函数不需要传递任何参数,则可以传递null参数或使用一个元素数为0的数组。

在调用Constructor.Invoke方法时,可能会遇到NotSupportedException异常,因为某些类型(如System.TypedReference, System.ArgIterator和System.RuntimeArgumentHandle)不能使用Invoke方法来创建实例。此外,还可能会出现SecurityException异常,这是由于调用者没有足够的代码访问权限造成的。

需要注意的是,Invoke方法只能用于创建具有公共访问级别的构造函数创建的对象。如果要创建私有构造函数创建的对象,则需要改用Activator.CreateInstance方法,该方法可以指定创建私有构造函数对象的访问级别。



class Person
    {
        //字段
        private int id = 1001;
        private string name = "jack";
        private int age = 30;
        private static string nation;


        //属性 
        public int Id { get => id; set => id = value; }
        public string Name { get => name; set => name = value; }
        public int Age { get => age; set => age = value; }
        public static string Nation { get => nation; set => nation = value; }

        //构造函数
        public Person()
        { 
        
        }
        public Person(int id, string name, int age)
        {
            this.id = id;
            this.name = name;
            this.age = age;
        }
        static Person()
        {
            nation = "新加坡";
        }

        //重写ToString ()
        public override string ToString()
        {
            return $" Person类:[id={this.id} name={this.name} age={this.age}]";
        }
    }

    class Program 
    {
        static void Main(string[] args)
        {
            Type type = Type.GetType("反射与依赖注入.Person");
            //获取公有的构造函数
            //ConstructorInfo[ ] constructorInfos= type.GetConstructors();  //取得公有的 非静态的构造函数
            //获取静态 非公有的 构造函数 
            ConstructorInfo[] constructorInfos = type.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic);
            foreach (ConstructorInfo constructor in constructorInfos)
            {
                Console.WriteLine($"构造函数名:{constructor.Name} 构造函数参数个数:{ constructor.GetParameters().Length} 是否静态:{constructor.IsStatic} ");
            }
            // 获取指定的构造函数 
            ConstructorInfo constructorInfo = type.GetConstructor(new Type[] { typeof(int),typeof(string),typeof(int) });

            //创建对象 
            //constructorInfo.Invoke(new object[] { 1002,"lucy",28});  //把原来的默认值更改了
             //Invoke创建对象,并返回此对象  但是返回的是object 
       Person    person=  constructorInfo.Invoke(new object[] { 1002, "lucy", 28 })  as Person;
            Console.WriteLine(person.ToString()); // ToString 默认的是输出 父类的全名,
                                                  // 如果想要通过ToString输出 这类的字段的值,就重写ToString 
                                                  //ToString()方法是Object类的一个虚拟方法

            //获取静态构造函数
      ConstructorInfo cons=    type.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic)[0];
            cons.Invoke(null);
            //获取
            Console.WriteLine(Person.Nation);
        }
    }

我这里有个报错 和两个警告

 cons.Invoke(null); 

System.MemberAccessException异常,错误消息为“Type initializer was not callable.
不过这样写 还是报错。

      Assembly.Load("ReflectionTest");
   Type type = Type.GetType("ReflectionTest.Person, ReflectionTest"); // full namespace and class name       

这是一个关于 .NET Core 目标框架不匹配的警告提醒。在您的 ReflectionTest 项目中,你使用了 Microsoft.Extensions.DependencyInjection.Abstractions 版本为7.0.0 的 NuGet 包。
该警告指出,此版本 NuGet 包不支持.NET Core3.1,并且没有经过测试。如果您想解决此问题,应该升级您的项目的目标框架为 .NET Core6.0 或更高版本,以保证包的兼容性。

要解决此问题,可以考虑以下两种方法之一:

1.升级项目的 .NET Core 目标框架版本到6.0或更高版本。

2.在项目文件中设置 <SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>,以忽略这个警告并尝试在不支持的配置下运行。

请注意,如果您选择第二种方法,则会存在 execution-time 错误的风险,因为您的项目使用的 NuGet 包与其目标框架不兼容。

建议您优先考虑升级到最新的 .NET Core 版本和相关 NuGet 包版本来确保最大程度上的代码兼容性和维护性。


[视频学习:【107-反射之Method】B站:IT萌叔Jack ] (https://www.bilibili.com/video/BV1rg4y1t7QX/?share_source=copy_web&vd_source=deaf604b58cb5a6dbd34d697e9efa138)

BindingFlags.DeclaredOnly是一个枚举值,在C#中被用于反射机制中。它表示只绑定已声明的成员,不包括继承自基类或实现接口的成员。

使用BindingFlags.DeclaredOnly标志后,反射的获取成员信息的范围将被限制为当前类型所声明的成员

例如,如果一个类A继承自另外一个类B,并且实现了一个接口I,那么如果使用BindingFlags.DeclaredOnly标志进行反射时,只能获取A类本身定义的成员,而不能获取从B类和I接口中继承或实现的成员。这种方式通常被用于一些特定场景,比如只想获取当前类中显式声明的成员信息,而不需要获取继承自父类的同名成员信息,以此提高程序搜索性能。


    class  Person
    {
        private string name;
        private int id;
        private static string nation;


        public static string Nation { get => nation; set => nation = value; }
        public string Name { get => name; set => name = value; }
        public int Id { get => id; set => id = value; }

        public Person()
        {
            nation = "新加坡";
        }
        public Person(int id, string name)
        {
            this.id = id;
            this.name = name;
        }
        public string Inroduce()
        {
            return $"我是{this.Name} 编号{this.Id}  ";
        }
        //静态带参函数 
        public static void ShowNation(string nation)
        {
            Nation = nation;
            Console.WriteLine($"国籍:{Nation}");
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            Type type = typeof(ReflectionTest.Person);
            //获取公有函数
            //MethodInfo[] methodInfos = type.GetMethods();
            //获取private函数 DeclaredOnly 自己声明的
            MethodInfo[] methodInfos = type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public);
            //遍历
            foreach (MethodInfo methodInfo in methodInfos)
            {
                Console.WriteLine($"函数返回值:{ methodInfo.ReturnType}  函数名:{ methodInfo.Name}  函数参数长度:{methodInfo.GetParameters().Length}");
            }

            //获取指定的函数
            //MethodInfo method=   type.GetMethod("Introduce",null);
            //method.Invoke(new Person(1003,"peter"), null);
            //Console.WriteLine(       method.Invoke(new Person(1003,"peter"), null)   );

            MethodInfo method = type.GetMethod("Introduce", new Type[] { });
            Console.WriteLine(method.Invoke(new Person(1003, "peter"), new object[] { }));

            //获取静态函数
            MethodInfo info = type.GetMethod("ShowNation", new Type[] { typeof(string)});
            info.Invoke(null,new object[] { "中国"}  );
        }
    }


C#语言入门详解 刘铁猛 接口隔离,反射,特性,依赖注入

CSDN:鲜鱼汤是我了
C#里的反射,特性和依赖注入(接口隔离)利用实例代码来解释说明

posted @ 2023-03-05 02:32  专心Coding的程侠  阅读(75)  评论(0编辑  收藏  举报