当前位置: 首页 > article >正文

C#特性和反射

1。特性概念理解?

特性(Attribute)是用于在【运行时】传递程序中各种元素(比如类、属性、方法、结构、枚举、组件等)行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

特性是【运行时】给各种元素添加【声明性标签】。语法:[某个特性]。 声明性标签:官方称为元数据,即:metadata
元数据:保存在程序集中有关程序及其类型的数据。元数据主要用来描述C#中各种对象(类,方法,构造函数,属性等)。

// 特性主要用来给各种对象添加元数据。反射主要用来拿各种对象的元数据。

特性(Attribute)用于给对象添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.
Net 框架提供了两种主要特性:预定义特性和自定义特性。

问:属性和特性啥关系?没关系。两个概念。属性Property,是类的成员。特性Attribute,用来描述类,方法等对象的元数据。

常用的特性:

AttributeUsage,Conditional,Obsolete,Category,Description,Browsable,DefaultValue,Serializable,在学习自定义控件时会用到部分特性。
// 多个特性无顺序,多个中括号设置
// Description特性用来给属性添加描述信息(注释,解释)
// Category特性用来给属性分类,默认分类放到“杂项”
// Browsable特性用来控制属性能否在属性窗口中出现

三种预定义特性分类:

• AttributeUsage预定义特性

主要让开发者在自定义特性时,控制自定义特性的应用范围,掌握
预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。

规定该特性的语法如下:

[AttributeUsage(
   validon,
   AllowMultiple=allowmultiple,
   Inherited=inherited
)]

其中:

参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。
参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。
例如:

// AttributeUsage特性,控制定义的特性在哪些对象上使用。 
//即:自定义的特性MyAttribute可以应用到哪些目标上。Usage使用
// 平时自定义的特性最想用到:类上,方法,属性。
// AttributeTargets特性应用的目标。Targets目标
// AllowMultiple = true控制特性是否能在同一个元素上应用多次,
//false只能应用一次,是默认情况,true可能应用多次
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property, 
AllowMultiple = true)]

• Conditional带条件特性

(了解,涉及到预定义指令)
这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符。

它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。例如,当调试代码时显示变量的值。

规定该特性的语法如下:

[Conditional(
   conditionalSymbol
)]
例如:

[Conditional("DEBUG")]

下面的实例演示了该特性:

实例

#define DEBUG
using System;
using System.Diagnostics;
public class Myclass
{
    [Conditional("DEBUG")]
    public static void Message(string msg)
    {
        Console.WriteLine(msg);
    }
}
class Test
{
    static void function1()
    {
        Myclass.Message("In Function 1.");
        function2();
    }
    static void function2()
    {
        Myclass.Message("In Function 2.");
    }
    public static void Main()
    {
        Myclass.Message("In Main function.");
        function1();
        Console.ReadKey();
    }
}

当上面的代码被编译和执行时,它会产生下列结果:

In Main function.
In Function 1.
In Function 2.

• Obsolete废弃特性

掌握
这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。

规定该特性的语法如下:

[Obsolete(
   message
)]
[Obsolete(
   message,
   iserror
)]

其中:

参数 message,是一个字符串,描述项目为什么过时以及该替代使用什么。
参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。
下面的实例演示了该特性:

实例

using System;
public class MyClass
{
   [Obsolete("Don't use OldMethod, use NewMethod instead", true)]
   static void OldMethod()
   {
      Console.WriteLine("It is the old method");
   }
   static void NewMethod()
   {
      Console.WriteLine("It is the new method");
   }
   public static void Main()
   {
      OldMethod();
   }
}

当您尝试编译该程序时,编译器会给出一个错误消息说明:

 Don't use OldMethod, use NewMethod instead

如何自定义一个特性?

1。命名建议以Attribute结尾,建议使用大驼峰。
2。必须继承基类Attribute
3。使用AttributeUsage特性来控制自定义的特性的应用范围。

观察:怎么判断对象是一个特性呢?看对象的结尾是否以Attribute结尾,只要以Attribute结尾的基本上是特性。
特性在定义时,建议以Attribute结尾。特性肯定是一个类,必须继承Attribute。Atribute是特性的基类。

2。反射?

反射是指在程序运行中,查看、操作其他程序集或者自身的元数据的各种信息(类、方法,属性、变量、对象等)的行为。C#中的反射(Reflection)是一种强大的功能,允许你在运行时检查和操作程序集、类型和对象的信息,基本上,使用反射可以在代码运行时检查和操作指定的类及其成员。C#反射的原理主要基于元数据(与C#特性相似),即程序集中存储的有关类型、方法等的信息。因为反射可以在程序编译后获得信息,所以它提高了程序的拓展性和灵活性。

反射就是为了拿到各种元素对应的标签。Reflection反射。使用反射时,代码性能低,因为反射使用了装箱和拆箱。

 // 1。反射拿类型
 Type type3 = typeof(Student);

 Student t = new Student();  // 没有使用反射,直接创建实例
 Type type4 = t.GetType();

// 2。通过类型获取字段,属性,方法,
 // 通过反射创建实例
 // Assembly.Load("1.特性")加载程序集,CreateInstance(完整的对象的名称)用加载的程序集在创建一个程序集中的对象的实例
 // 如果加载的程序集在当前项目中的bin/debug中不存在,会加载失败。
  Assembly assembly = Assembly.Load("1.特性"); // 拿程序集
 // 使用反射技术创建程序集中的某个类的实例。装箱的操作
 object t1 = assembly.CreateInstance("_1.特性.Student");  // 类似于Student t = new Student();
 
  //FieldInfo fieldInfo = type3.GetField("_id"); // 获取单个字段
 //fieldInfo.SetValue(t1, 1);  // 给字段设置值   相当于_id=10, 报错:私有的无法访问
 FieldInfo fieldInfo = type3.GetField("myId");
 int myId = (int)fieldInfo.GetValue(t1);  // 获取公开的字段值,使用装箱和拆箱
 Console.WriteLine(myId);

 fieldInfo.SetValue(t1, 20); // 设置字段值
 int myId2 = (int)fieldInfo.GetValue(t1);
 Console.WriteLine(myId2);
  Console.WriteLine("-----------------");
 FieldInfo[] fieldInfos = type3.GetFields();
 foreach (var item in fieldInfos)
 {
     Console.WriteLine(item.GetValue(t1));
 }

 Console.WriteLine("-----------------");
 PropertyInfo propertyInfo1 = type3.GetProperty("Name"); // 拿单个属性
 propertyInfo1.SetValue(t1, "张三"); // 设置属性
 string name = (string)propertyInfo1.GetValue(t1); // 获取属性值
 Console.WriteLine(name);

 Console.WriteLine("-----------------");
 PropertyInfo[] propertyies = type3.GetProperties(); // 拿所有公开属性
 foreach (var item in propertyies)
 {
     if (item.Name == "Id") // 判断属性是不是Id属性
     {
         item.SetValue(t1, 100);
     }
     if (item.Name == "Name")
     {
         item.SetValue(t1, "李四");
     }
     Console.WriteLine(item.GetValue(t1));
 }

 Console.WriteLine("-----------------");
 MethodInfo methodInfo = type3.GetMethod("Method2");  // 拿方法
 methodInfo.Invoke(t1, new object[] { "ABC", 10000 }); // 调用方法


 Type h1 = type3.GetNestedType("Hello"); // 拿到Student类下的Hello类的类型
 object hIns = assembly.CreateInstance(h1.FullName);
 PropertyInfo pInfo = h1.GetProperty("Name");
 object value = pInfo.GetValue(hIns);
 Console.WriteLine(value);

 // 用反射的技术把libs/ClassLibrary1.dll中的Class1应用一下。
 // 直接使用不可能了,原因:此类库没有在当前程序中引用。
 // Class1 class1 = new Class1();  // 代码出错

 // 1。先加载程序集
 string assemblyFile = Path.Combine(Environment.CurrentDirectory, "../../libs/ClassLibrary1.dll");
 Assembly class1Assembly = Assembly.LoadFrom(assemblyFile);
 // 2。创建某个类的实例
 object class1 = class1Assembly.CreateInstance("ClassLibrary1.Class1");
 Type class1Type = class1.GetType();
 // 3。拿成员
 //FieldInfo
 //MethodInfo
 //PropertyInfo
 //MemberInfo其实是FieldInfo,MethodInfo,PropertyInfo等的综合体
 Console.WriteLine("-----------------");
 MemberInfo[] members = class1Type.GetMembers(); // 获取类的所有的成员
 foreach (var item in members)
 {
     // 判断某个成员是否是继承的成员, item.DeclaringType == item.ReflectedType非继承的成员
     if (item.DeclaringType == item.ReflectedType)
     {
         // 判断成员是否是属性
         if (item.MemberType == MemberTypes.Property)
         {
             PropertyInfo p = item as PropertyInfo;
             Console.WriteLine($"属性名:{p.Name},属性值:{p.GetValue(class1)}");
         }
         // 判断成员是否是方法
         if (item.MemberType == MemberTypes.Method)
         {
             
             MethodInfo m = item as MethodInfo;
             // IsSpecialName如果返回true表示是特殊的方法(由属性生成的get/set访问器。
             Console.WriteLine($"方法名:{m.Name},是否是特殊方法:{m.IsSpecialName}");
             if (!m.IsSpecialName)
             {
                 if (m.Name == "Method1")
                 {
                     Console.Write("执行结果:");
                     m.Invoke(class1, null);  // Invoke调用方法
                 }
                 else if (m.Name == "Method2")
                 {
                     object result = m.Invoke(class1, new object[] { "hello world" });
                     Console.WriteLine($"执行结果:{result}");
                 }
             }
         }
     };
 }

结果

在这里插入图片描述

反射重要的API?

t.GetField();// FieldInfo类,拿类的字段
t.GetProperty();// PropertyInfo类,拿类的属性
t.GetMethod(“SayHello”) // MethodInfo类,拿方法的元数据。
t.GetConstructor() // ConstructorInfo类,拿类的构造函数
t.GetEvent();// EventInfo类,拿类的事件
t.GetCustomAttribute();// Attribute类,拿类的特性
typeof() 和 GetType() // 获取类型
Invoke() 和 InvokeMember() // 调用相应的成员
SetValue(),GetValue()// 设置属性,获取属性


http://www.kler.cn/a/589768.html

相关文章:

  • MySQL高频八股——事务过程中Undo log、Redo log、Binlog的写入顺序(涉及两阶段提交)
  • 异常(11)
  • Linux 日志与时间同步指南
  • 2024浙江大学计算机考研上机真题
  • 【蓝桥杯】省赛:神奇闹钟
  • 自然语言处理(2)—— NLP之百年风雨路
  • Android第三次面试(Java基础)
  • 蓝牙系统的核心组成解析
  • Secs/Gem第一讲 · 总结精华版(基于secs4net项目的ChatGpt介绍)
  • TypeScript类型兼容性 vs JavaScript动态类型:深入对比解析
  • redis分片集群如何解决高并发写问题的?
  • 【2025年3月最新】Cities_Skylines:城市天际线1全DLC解锁下载与教程
  • 对项目进行优化
  • STL——vector
  • openai 标准化协议 Structured Outputs 具体示例教程
  • [蓝桥杯 2024 国 A] 最长子段
  • 虚幻基础:GAS
  • 2.4 python网络编程
  • Matlab 单球机器人动力学与LQR控制研究
  • 2025年03月11日Github流行趋势