0. 参考文档
- .NET中的CTS、CLS和CLR三者的关系和作用
- CTS,CLS,CLR解释
- .NET 委托(Delegate、Action、Func)
- C#委托的介绍(delegate、Action、Func、predicate)
- c#反射机制
- 程序集与反射技术(C#)
- c#程序集
- c#程序集
- c#特性
1. CTS,CLS,CLR
.NET平台下不只有C#语言,还有 VB.Net,F#等语言,IL是程序最终编译的可以执行的二进制代码(托管代码),不同的语言最终都编译成标准的IL(MSIL),这样C#可以调用VB.Net写的程序集(DLL之类的)
1.1 CTS:通用类型系统
不同语言中的数据类型各不相同,比如整数类型在VB.Net中是Integer,C#中是int,.Net平台规定了通用数据类型(CTS,Common Type System),各个语言编译器都把自己的类型编译成CTS中的类型。
定义了如何在.NET Framework运行库中声明、使用和管理类型,同时也是.NET Framework运行库支持跨语言集成的一个重要组成部分。.NET Framework类库中的通用类型系统支持两种类别的类型,分别为值类型和引用类型。
通用类型系统主要执行以下功能:①建立一个支持跨语言集成、类型安全和高性能代码执行的框架。 ②提供一个支持完整实现多种编程语言的面向对象的模型。③定义各语言必须遵守的规则,有助于确保用不同语言编写的对象能够发生交互作用。
1.2 CLS :公共语言规范
CLS(Common Language Specification)即公共语言规范,它是许多应用程序所需的一套基本语言功能。CLS 规则定义了通用类型系统的子集,即所有适用于公共类型系统的规则都适用于 CLS,除非 CLS 中定义了更严格的规则。CLS 通过定义一组开发人员可以确信在多种语言中都可用的功能来增强和确保语言的互用性。CLS 还建立了 CLS 遵从性要求,可帮助用户确定托管代码是否符合 CLS 以及一个给定的工具对托管代码(该代码是使用 CLS 功能的)开发的支持程度。
CLS 在设计上足够大,可以包括开发人员经常需要的语言构造;同时也足够小,大多数语言都可以支持它。此外,任何不可能快速验证代码类型安全性的语言构造都被排除在CLS 之外,以便所有符合 CLS 的语言都可以生成可验证的代码。
编程语言的区别不仅仅在于类型。例如,一些语言支持多继承性,一些语言支持无符号数据类型,一些语言支持运算符重载。用户应认识到这一点, 因此.NET通过定义公共语言规范(CLS:Common Language Specification),限制了由这些不同引发的互操作性问题。CLS制定了一种以.NET平台为目标的语言所必须支持的最小特征,以及该语言与其 他.NET语言之间实现互操作性所需要的完备特征。认识到这点很重要,这里讨论的特征问题已不仅仅是语言间的简单语法区别。例如,CLS并不去关心一种语 言用什么关键字实现继承,只是关心该语言如何支持继承。CLS是CTS的一个子集。这就意味着一种语言特征可能符合CTS标准,但又超出CLS的范畴。例 如:C#支持无符号数字类型,该特征能通过CTS的测试,但CLS却仅仅识别符号数字类型。因此,如果用户在一个组件中使用C#的无符号类型,就可能不能 与不使用无符号类型的语言(如VB.NET)设计的.NET组件实现互操作。
1.3 CLR:公共语言运行库
简单地说,CLR是CTS的实现,也就是说,CLR是应用程序的执行引擎和功能齐全的类库,该类库严格按照CTS规范实现。作为程序执行引 擎,CLR负责安全地载入和运行用户程序代码,包括对不用对象的垃圾回收和安全检查。在CLR监控之下运行的代码,称为托管代码(managed code)。
CLR是.NET Framework 的基础,可以将其看作一个在执行时管理代码的代理,它提供了内存管理、线程管理和远程处理等核心服务,并且还强制实施严格的类型安全以及可提高安全性和可靠性的其他形式的代码准确性。
公共语言运行库用于管理内存、线程执行、代码执行、代码安全验证、编译以及其他系统服务,这些功能是在公共语言运行库上运行的托管代码所固有的。至于安全性,则取决于包括托管组件的来源(如 Internet、企业网络或本地计算机)在内的一些因素,由于托管组件被赋予不同程度的信任,这意味着即使用在同一活动应用程序中,托管组件可能执行文件访问操作、注册表访问操作或其他须小心使用的功能,也可能不能够执行这些功能。
公共语言运行库还通过实现称为通用类型系统(CTS)的严格类型验证和代码验证基础结构来加强代码可靠性。CTS确保所有托管代码都可以自我描述。微软和第三方语言编译器生成符合 CTS的托管代码,这意味着托管代码可在严格实施类型保护和类型安全的同时使用其他托管类型和实例。
2. 委托 delegation
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递。
2.1 Delegate
常用到的一种声明,且至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型;
示例代码
// 定义委托类型及参数,表示有两个参数,并返回int型
protected delegate int ClassDelegate(int x, int y);
static void Main(string[] args)
{
ClassDelegate dele = new ClassDelegate(Add);//实例化一个委托
Console.WriteLine(dele(1, 2));//调用委托
Console.ReadKey();
}
static int Add(int a, int b)
{
return a + b;
}
2.2 Action
Action
是无返回值的泛型委托,至少0个参数,至多16个参数,无返回值;
Action
表示无参,无返回值的委托Action<int,string>
表示有传入参数int,string
无返回值的委托
简单示例
public void Test<T>(Action<T> action,T p)
{
action(p);
}
示例一:调用方法
static void Main(string[] args)
{
Action<int, int> ac = new Action<int, int>(ShowAddResult);//实例化一个委托
ac(1, 2);//调用委托
Console.ReadKey();
}
static void ShowAddResult(int a, int b)
{
Console.WriteLine(a + b);
}
示例二:使用
lambda
表达式
static void Main(string[] args)
{
Action<int, int> ac = ((p, q) => Console.WriteLine(p + q));//实例化一个委托
ac(1, 2);//调用委托
Console.ReadKey();
}
示例三:作为方法参数使用
static void Main(string[] args)
{
Action<string> ac = (p => Console.WriteLine("我是方法1,传入值:"+p));//实例化一个委托
Action<string> ac2 = (p => Console.WriteLine("我是方法2,传入值:" + p));//实例化另一个委托
Test(ac, "参数1");//调用test方法,传入委托参数
Test(ac2, "参数2");//调用test方法,传入委托参数
Console.ReadKey();
}
static void Test<T>(Action<T> ac, T inputParam)
{
ac(inputParam);
}
2.3 Func
Func
是有返回值的泛型委托,至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void
Func<int>
表示无参,返回值为int
的委托Func<object,string,int>
表示传入参数为object, string
返回值为int
的委托Func<T1,T2,,T3,int>
表示传入参数为T1,T2,,T3
(泛型)返回值为int
的委托
简单示例
public int Test<T1,T2>(Func<T1,T2,int>func,T1 a,T2 b)
{
return func(a, b);
}
示例一:调用方法
static void Main(string[] args)
{
Func<string> fc1 = new Func<string>(ShowAddResult);//实例化一个委托
string result = fc1();//调用委托
Console.WriteLine(result);
Console.ReadKey();
}
static string ShowAddResult()
{
return "地球是圆的";
}
示例二:使用
lambda
表达式
static void Main(string[] args)
{
//实例化一个委托,注意不加大括号,写的值就是返回值,不能带return
Func<string> fc1 = () => "地球是圆的";
//实例化另一个委托,注意加大括号后可以写多行代码,但是必须带return
Func<string> fc2 = () =>
{
return "地球是圆的";
};
string result = fc1();//调用委托
string result2 = fc2();//调用委托
Console.WriteLine(result);
Console.WriteLine(result2);
Console.ReadKey();
}
示例三:作为方法参数传递
static void Main(string[] args)
{
//实例化一个委托,注意不加大括号,写的值就是返回值,不能带return
Func<int, string> fc1 = (p) => "传入参数" + p + ",地球是圆的";
//实例化另一个委托,注意加大括号后可以写多行代码,但是必须带return
Func<string, string> fc2 = (p) =>
{
return "传入参数" + p + ",地球是圆的";
};
string result = Test<int>(fc1, 1);//调用委托
string result2 = Test<string>(fc2, "1");//调用委托
Console.WriteLine(result);
Console.WriteLine(result2);
Console.ReadKey();
}
static string Test<T>(Func<T, string> fc, T inputParam)
{
return fc(inputParam);
}
static void Main(string[] args)
{
Console.WriteLine(Test<int,int>(Fun,100,200));
Console.ReadKey();
}
public static int Test<T1, T2>(Func<T1, T2, int> func, T1 a, T2 b)
{
return func(a, b);
}
private static int Fun(int a, int b)
{
return a + b;
}
2.4 Predicate
Predicate
是返回bool
型的泛型委托,至少1个参数,至多1个参数,返回值固定为bool
Predicate<int>
表示传入参数为int
返回bool
的委托
3. Lambda表达式
3.1 Lambda表达式定义
Lambda
表达式实际上是一种匿名函数,在 Lambda
表达式中可以包含语句以及运算等操作。并且可用于创建委托或表达式目录树类型,支持带有可绑定到委托或表达式树的输入参数的内联表达式。
使用 Lambda
表达式来创建匿名函数。 使用 lambda
声明运算符=>
从其主体中分离 lambda
参数列表。
3.2 表达式 lambda
表达式位于 =>
运算符右侧的 lambda
表达式称为“表达式 lambda
”。 表达式 lambda
会返回表达式的结果,并采用以下基本形式:
(input-parameters) => expression
我们经常在 LINQ
查询中使用 Lambda
表达式,如作为 Where<TSource>
的参数:
public static IEnumerable<TSource> Where<TSource>
(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
参数是委托类型 Func<TSource, bool> predicate)
,这里使用 Lambda
表达式进行创建我想应该是最合适的
注意:在 is
或 as
运算符的左侧不允许使用 Lambda
表达式
3.3 语句 lambda
语句 lambda
,语句块作为其主体:
(input-parameters) => { <sequence-of-statements> }
4. 反射 Reflection
审查元数据并收集关于它的类型信息的能力称为反射。元数据(编译以后的最基本数据单元)就是一大堆的表,当编译程序集或者模块时,编译器会创建一个类定义表,一个字段定义表,和一个方法定义表等。
反射提供了封装程序集、模块和类型的对象(Type 类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了属性,可以利用反射对它们进行访问。
反射通常具有以下用途:
- 使用
Assembly
定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例 - 使用
Module
了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。 - 使用
ConstructorInfo
了解构造函数的名称、参数、访问修饰符(如pulic
或private
)和实现详细信息(如abstract
或virtual
)等。使用Type
的GetConstructors
或GetConstructor
方法来调用特定的构造函数。 - 使用
MethodInfo
了解方法的名称、返回类型、参数、访问修饰符(如pulic
或private
)和实现详细信息(如abstract
或virtual
)等。使用Type
的GetMethods
或GetMethod
方法来调用特定的方法。 - 使用
FiedInfo
了解字段的名称、访问修饰符(如public
或private
)和实现详细信息(如static
)等,并获取或设置字段值。 - 使用
EventInfo
了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。 - 使用
PropertyInfo
了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。 - 使用
ParameterInfo
了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。
反射常用类
反射是一个程序集发现及执行的过程,通过反射能够得到.exe或.dll等程序集内部的信息。使用反射能够看到一个程序集内部的接口、类、方法、字段、属性、特性等等信息。
类型 | 描述 |
---|---|
Assembly | 通过此类能够载入操纵一个程序集,并获取程序集内部信息 |
FieldInfo | 该类保存给定的字段信息 |
MethodInfo | 该类保存给定的方法信息 |
MemberInfo | 该类是一个基类,定义了EventInfo,FieldInfo,MethodInfo,PropertyInfo的多个公用行为 |
Module | 该类能够使你能訪问多个程序集中的给定模块 |
ParameterInfo | 该类保存给定的參数信息 |
PropertyInfo | 该类保存给定的属性信息 |
4.1 Assembly 程序集对象
System.Reflection.Assembly
:表示一个程序集
程序集是代码进行编译是的一个逻辑单元,把相关的代码和类型进行组合,然后生成PE文件(例如可执行文件.exe和类库文件.dll)。由于程序集在编译后并不一定会生成单个文件,而可能会生成多个物理文件,甚至可能会生成分布在不同位置的多个物理文件,所以程序集是一个逻辑单元,而不是一个物理单元。即程序集在逻辑上是一个编译单元,但在物理储存上可以有多种存在形式。对于静态程序集可以生成单个或多个文件,而动态程序集是存在于内存中的。在C#中程序集处处可见,因为任何基于.NET的代码在编译时都至少存在一个程序集(所有.NET项目都会默认引用mscorlib
程序集)
程序集包含了两种文件:可执行文件(.exe
文件)和 类库文件(.dll
文件)
在VS开发环境中,一个解决方案可以包含多个项目,而每个项目就是一个程序集。
应用程序结构
包含应用程序域(AppDomain
),程序集(Assembly
),模块(Module
),类型(Type
),成员(EventInfo、FieldInfo、MethodInfo、PropertyInfo
) 几个层次
获得程序集的方式
获得当前【应用程序域】中的所有程序集
Assembly[] ass = AppDomain.CurrentDomain.GetAssemblies();
获得当前对象所属的类所在的程序集
this.GetType().Assembly;
根据路径加载程序集
Assembly.LoadFrom(@"E:SolutionRPDMOinDebugDMO.dll");//或使用LoadFile()方法
Assembly
相关属性
属性名/方法 | 描述 |
---|---|
FullName | 程序集全名 |
CodeBase | 程序集初始位置 |
Location | 程序集位置 |
EntryPoint | 程序集入口 |
GetTypes() | 获取程序集里所有的类型 |
Assembly.LoadFile
与Assembly.LoadFrom
的差别
LoadFile
方法用来来载入和检查具有同样标识但位于不同路径中的程序集。但不会载入程序的依赖项LoadFrom
不能用于载入标识同样但路径不同的程序集
4.2 Module 程序集模块
Assembly assembly = Assembly.Load("mscorlib");//加载程序集
Module module = assembly.GetModule("CommonLanguageRuntimeLibrary");//得到指定模块
Console.WriteLine("模块名:" + module.Name);
Type[] types = module.FindTypes(Module.FilterTypeName, "Assembly*");
foreach (var item in types)
{
Console.WriteLine("类名:" + item.Name);//输出类型名
}
Console.Read();
4.3 Type 类型
一个类对应一个Type
对象
通过类获得对应的
Type
Type t1 = typeof(UserInfo);
Assembly对象,通过类的
full name
类获得Type
对象
Assembly assem = Assembly.LoadFrom(@"E:SolutionRPDMOinDebugDMO.dll");
Type type = assem.GetType("DMO.UserInfo");
object obj = Activator.CreateInstance(type); // 创建实例
获得程序集中定义的所有的
public
类
Type[] allPublicTypes = ass1.GetExportedTypes();
获得程序集中定义的所有的类
Assembly.LoadFrom(assPath);
创建实例
Type t = typeof(UserInfo);//得到类型
object o = Activator.CreateInstance(t);//创建类型的实例
Console.WriteLine("已创建Myclass对象:" + o);
MethodInfo method = t.GetMethod("Show");//获得实例的方法
method.Invoke(o, new object[] { 100 });//调用方法
Type
相关属性
Type type = typeof(UserInfo);
Console.WriteLine("类型名:" + type.Name);
Console.WriteLine("类全名:" + type.FullName);
Console.WriteLine("命名空间名:" + type.Namespace);
Console.WriteLine("程序集名:" + type.Assembly);
Console.WriteLine("模块名:" + type.Module);
Console.WriteLine("基类名:" + type.BaseType);
Console.WriteLine("是否类:" + type.IsClass);
Console.WriteLine("类的公共成员:");
MemberInfo[] memberInfos = type.GetMembers();//得到所有公共成员
foreach (var item in memberInfos)
{
Console.WriteLine("成员类型:" + item.MemberType + " 成员:" + item);
}
Type
类的属性
属性名 | 描述 |
---|---|
t.Assembly | 获取t所在的程序集 |
t.FullName | 获取t所对应的类的full name |
t.Name | 获取t所对应的类的 name |
t.IsArray | 判断t是否是一个数组类 |
t.IsEnum | 判断t是否是一个枚举类 |
t.IsAbstract | 判断t是否是一个抽象类 |
t.IsInterface | 判断t是否是一个interface |
t.IsSubclassOf(Type parent) | t是否是parent的子类 |
t.IsInstanceOfType(object o) | o是否是t类的对象 |
t.GetFields() | method, property 得到所有的public的fields,methods,properties |
Type
类的方法
实例代码
static void TypeTest1()
{
Person p = new Person { Name = "NaNa", Age = 5 };
Type typePerson = p.GetType();
//搜索具有指定名称的公共属性
PropertyInfo pf = typePerson.GetProperty("Name");
pf.SetValue(p, "LiLi", null);
Console.WriteLine(p.Name);
//返回所有公共属性
PropertyInfo[] props = typePerson.GetProperties();
StringBuilder builder = new StringBuilder(30);
foreach (PropertyInfo item in props)
{
builder.Append(item.Name + "=" + item.GetValue(p, null) + "
");
}
builder.Append("----------------------
");
//返回所有公共字段
FieldInfo[] fieIds = typePerson.GetFields();
foreach (FieldInfo item in fieIds)
{
builder.Append(item.Name + "=" + item.GetValue(p) + "
");
}
builder.Append("----------------------
");
// 根据参数返回指定方法
var m1 = t.GetMethod("Show",new Type[0]); // 返回无参数的方法
// 返回参数为int的方法,多个参数都好隔开
var m2 = t.GetMethod("Show",new Type[] { typeof(int)});
//返回所有公共方法
MethodInfo[] methods = typePerson.GetMethods();
foreach (MethodInfo item in methods)
{
builder.Append(item + "
");
}
builder.Append("----------------------
");
Console.WriteLine(builder);
// 根据参数返回指定构造方法
var con = typePerson.GetConstructor(new Type[0]); // 返回无参构造方法
// 返回参数只有一个int的构造方法,多个参数都好隔开
var con2 = typePerson.GetConstructor(new Type[] { typeof(int)});
//返回所有公共构造函数
ConstructorInfo[] cons = typePerson.GetConstructors();
foreach (ConstructorInfo item in cons)
{
//Name都是 .ctor
Console.WriteLine(item.Name + "
");
//构造函数的参数个数
Console.WriteLine(item.GetParameters().Length + "
");
ParameterInfo[] parames = item.GetParameters();
foreach (var pars in parames)
{
Console.WriteLine(pars.Name+":"+pars.ParameterType);
}
}
}
使用示例
Type type = typeof(UserInfo);//得到类型
object obj = Activator.CreateInstance(type);
var prop_id = type.GetProperty("id");
prop_id.SetValue(obj,1);
var methos_show = type.GetMethod("Show",new Type[0]);
methos_show.Invoke(obj,new object[0]);
5. 特性 Attribute
特性(Attribute
)本质上是一个类,其为目标元素(比如类、方法、结构、枚举、组件等)提供关联附加信息,并在运行期以反射的方式来获取附加信息。
说明:特性类的实例里没有验证逻辑,只有验证用到的规范数据(比如字符串长度)、提示信息等。验证逻辑需要自己写。
.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
创建并使用自定义特性包含四个步骤:
- 声明自定义特性
- 构建自定义特性
- 在目标程序元素上应用自定义特性
- 通过反射访问特性
5.1 预定义特性
AttributeUsage
限定
AttributeUsage
是 Attribute
的 Attribute
,指定Attribute
限制用于哪类实体上,在这里,实体是指: class
、method
、constructor
、field
、property
、GenericParameter
或者用All
,表明可用于所有实体。每个target
标记可以用|
链接,如AttributeTargets.Class|AttributeTargets.Method
表示可用于class
或者method
上。
无限定
[AttributeUsage(AttributeTargets.All)]
public class AllTargetsAttribute : Attribute {}
限定只能标记在类上
[AttributeUsage(AttributeTargets.Class)]
public class ClassTargetAttribute : Attribute {}
限定只能标记在方法上
[AttributeUsage(AttributeTargets.Method)]
public class MethodTargetAttribute : Attribute {}
限定只能标记在构造函数上
[AttributeUsage(AttributeTargets.Constructor)]
public class ConstructorTargetAttribute : Attribute {}
限定只能标记在字段上
[AttributeUsage(AttributeTargets.Field)]
public class FieldTargetAttribute : Attribute {}
限定只能标记在泛型类型参数上
[AttributeUsage(AttributeTargets.GenericParameter)]
public class GenericParameterTargetAttribute : Attribute {}
5.2 自定义特性
一个新的自定义特性应派生(继承)自 System.Attribute
类
public class CustomAttribute : Attribute
{
public int length { get; set; }
}