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()// 设置属性,获取属性