反射-Type类
- 什么是元数据,什么是反射
- 程序是用来处理数据的,文本和特性都是数据,而程序本身(类的定义和BLC中的类)这些也都是数据
- 有关程序及其类型的数据被称为元数据(metadata),它们保存在程序的程序集中
- 程序在运行时,可以查看其他程序集或其本身的元数据。一个运行的程序查看本身的元数据或者其他程序集的元数据的行为叫做反射
- Type类
- 预定义类型(int long 和string等),BCL中的类型(Console,IEnumerable等)和自定义类型(MyClass,MyDel等)。每种类型都有自己的成员和特性
- BCL声明了一个叫做Type的抽象类,它被设计用来包含类型的特性。使用这个类的对象能够让我们获取程序使用的类型的信息
- 由于Type是抽象类,因此不能利用它去实例化对象
- 关于Type的重要事项如下:
- 对于程序中用到的每一个类型,CLR都会创建一个包含这个类型信息的Type类型的对象
- 程序中用到的每一个类型都会关联到独立的Type类的对象
- 不管创建的类型有多少个示例,只有一个Type对象会关联到所有这些实例
实例:
MyClass类
namespace _034_反射和特性Type类
{
class MyClass
{
public int id;
public int age;
public string Name1 { get; set; }
public string Name2 { get; set; }
public void Test1() { }
public void Test2() { }
}
}
Main方法
using System.Reflection; //添加命名空间
class Program
{
static void Main(string[] args)
{
//每一个类对应一个Type对象,这个Type对象包含了这个类的方法,成员等
//对象包含着所属类各个成员具体的值,Type对象只包含这些成员(不包含具体值)
MyClass myClass = new MyClass() ;
Type type = myClass.GetType(); //通过对象获取到这个对象所属类的Type对象
Console.WriteLine(type.Name); //类的名字
Console.WriteLine(type.Namespace); //所在命名空间
Console.WriteLine(type.Assembly); //所在程序集
//需要添加命名空间
FieldInfo[] array = type.GetFields(); //获取类的字段成员
PropertyInfo[] array2 = type.GetProperties(); //获取属性
MethodInfo[] array3 = type.GetMethods(); //获取方法
foreach (FieldInfo info in array){Console.Write(info.Name + " ");}
foreach (PropertyInfo info in array2){Console.Write(info.Name + " ");}
foreach (MethodInfo info in array3) { Console.Write(info.Name + " "); }
//属性的getset方式是编译器生成的
Console.ReadKey();
}
}
Assembly程序集类
- Assembly类在System.Reflection命名空间中定义,它允许访问给定程序集的元数据,也包含了可以加载和执行程序集。
- 如何加载程序集:
- Assembly assembly1=Assembly.Load(“SomeAssembly”);
根据程序集的名字加载程序集,会在本地目录和全局程序集缓存目录查找符合名字的程序集
- Assembly assembly2=Assembly.LoadFrom(@“C:\xx\xx\SomeAssembly.dll”);
这里的参数是程序集的完整路径名,它不会在其他位置搜索
- 通过类创建的对象加载程序集
using System.Reflection;
class Program
{
MyClass myClass = new MyClass();
Assembly assem = myClass.GetType().Assembly;
Console.WriteLine(assem.FullName);
Type[] types = assem.GetTypes(); //获取程序集中定义的类
}
特性
特性(attribute)是一种允许我们向程序集增加元数据的语言结构。它是用于保存程序结构信息的某种特殊类型的类。
- 将应用了特性的程序结构叫做目标
- 设计用来获取和使用元数据的程序(对象浏览器)叫做特性的消费者(被调用的时候)
- .NET预定了很多特性,我们也可以自己声明自定义特性
Obsolete特性
使用Obsolete特性将程序结构标注为过期的,并且在代码编译时,显示有用的警告信息
class Program
{
[Obsolete("这个方法已经被弃用了")] //Obsolete表示这个方法被弃用了,并出现提醒,不会报错
static void OldMethod1() { }
[Obsolete("这个方法不能用了",true)]//后面true表示禁止使用(会报错)
public void OleMethod2() { }
static void Main(string[] args)
{
OldMethod1(); //警告+提醒
OleMethod2(); //报错
Console.ReadKey();
}
}
Conditional特性
- Conditional特性允许我们包括或取得特定方法的所有调用,为方法声明应用conditional特性并把编译符作为参数来使用
- 定义方法的CIL代码本身总是会包含在程序集中,只是调用代码会被插入或忽略
(代码会被编译放入程序集中,只是不被调用)
#define IsTest //定义一个宏,来启用被Conditional注释的方法
using System.Diagnostics; //添加命名空间
class Program
{
[Conditional("IsTest")] //相当于把方法注释掉(启用时需要定义宏)
static void Test1() { Console.WriteLine("Test1"); }
static void Test2() { Console.WriteLine("Test2"); }
static void Main(string[] args)
{
Test1();
Test2();
Console.ReadKey();
}
}
调用者信息特征
调用者信息特征可以访问文件路径,代码行数,调用成员的名称等源代码信息。
这个特性只能用于方法中的可选参数
using System.Runtime.CompilerServices; //添加命名空间
class Program
{
//需要添加命名空间,同时需要给定初始值
//CallerFilePath输出所在文件的路径
//CallerLineNumber输出调用此方法的行数
//CallerMemberName输出此方法所在的函数名
static void PrintOut(string str,[CallerFilePath] string fileName="",
[CallerLineNumber]int lineNumber=0,[CallerMemberName]string memberName="")
{
Console.WriteLine(str);
Console.WriteLine(fileName);
Console.WriteLine(lineNumber);
Console.WriteLine(memberName);
}
static void Main(string[] args)
{
Console.ReadKey();
}
}
DebuggerStepThrough
DebuggerStepThrough特性告诉调试器在执行目标代码时不要进去该方法调试**(确定该方法毫无疑问是正确的)**
//当我们确定这个方法没有任何错误的时候,可以跳过断点的逐语句调试(相当于逐过程断点调试)
[DebuggerStepThrough]
static void Test1(){}
其他预定义特性
| 特性 | 意义 |
|---|---|
| CLSCompiant | 声明可公开的成员应该被编译器检查是否符合CLS。兼容的程序据可以被任何.NET兼容的语言使用 |
| Serializable | 声明结构可以被序列化 |
| NonSerialized | 声明结构不可以被序列化 |
| DLLImport | 声明是非托管代码实现的 |
| WebMethod | 声明方法应该被作为XML Web服务的一部分暴露 |
| ArrributeUsage | 声明特性应用到什么类型的程序结构。将这个特性应用到特性声明上 |
创建自定义特性

MyTestAttribute特性类:
//1.特性类的后缀要以Attribute结尾,比如"MyTest+Attribute"
//2.需要继承自System.Attribute
//3.一般情况下声明为sealed
//4.一般情况下,特性类用来表示目标结构的一些状态(定义一些字段或者属性,一般不定义方法)
[AttributeUsage(AttributeTargets.Class)]
//表示这个特性类的使用目标(可以应用到的程序结构有哪些)
//创建一个特性就相当于创建一个特性类
class MyTestAttribute :System.Attribute
{
public string Description { get; set; }
public string VersionNumber { get; set; }
public int ID { get; set; }
public MyTestAttribute(string des)
{
this.Description = des;
}
}
Main方法:
using _035; //添加命名空间
//使用特性类的时候相当于调用特性类的构造函数(需要添加命名空间)
//使用特性的时候,名字后面的Attribute不用写
//[MyTest("自定义的简单特性")]
//命名参数:可以通过指定属性的名字,给属性赋值
[MyTest("命名参数",ID =100)]
class Program
{
static void Main(string[] args)
{
Type type = typeof(Program);
//是否搜索继承类所使用的特性(Program继承自object类)
object [] array = type.GetCustomAttributes(false);
MyTestAttribute myTest = array[0] as MyTestAttribute;
Console.WriteLine(myTest.Description);
Console.WriteLine(myTest.ID);
Console.ReadKey();
}
}
C#编程:深入理解反射和特性-Type类
本文详细介绍了C#中的反射和Type类,揭示了元数据的概念以及如何利用反射查看和操作程序集的元数据。讨论了Assembly类的使用,包括加载程序集的不同方式。此外,文章还探讨了特性,如Obsolete和Conditional,以及如何创建自定义特性,为代码添加元数据。
1508

被折叠的 条评论
为什么被折叠?



