C#高级
程序集
程序集概念:
程序集是.net中的概念。
.net中的dll与exe文件都是程序集。(exe与dll的区别(exe有程序主入口,可以执行,dll没有主入口,不可运行))
程序集(Assembly),可以看做是一堆相关类打一个包,相当于java中的jar包(*)
程序集的内容:
程序集包含:
类型元数据(描述在代码中定义的每一类型和成员,二进制形式)
程序集元数据(程序集清单、版本号、名称等)
IL代码(这些都被装在exe或dll中)
资源文件
每个程序集都有自己的名称、版本等信息。这些信息可以通过AssemblyInfo.cs文件来自己定义
程序集的好处:
程序中只引用必须的程序集,减小程序的尺寸。
程序集可以封装一些代码,只提供必要的访问接口。
添加程序集引用:
添加路径、项目引用、GAC(全局程序集缓存)(了解)
不能循环添加引用
在c#中添加其他语言编写的dll文件的引用。(参考P/Invoke,在.net中调用非程序集的dll)
反射
反射:
就是动态获取程序集中的元数据来操作类型
反射中的一个重要的类(type):
获取type的来那个中方法:
通过类获得Type:Type t = typeof(Person)
通过对象获得类的Type:Type t = p.GetType()
Type类的一些常用验证方法:
bool IsAssignableFrom(Type c):(直译:是否可以从c赋值)判断当前的类型的变量是不是可以接受c类型变量的赋值。
bool IsInstanceOfType(object o):判断对象o是否是当前类的实例(当前类可以是o的类、父类、接口)
bool IsSubclassOf(Type c):判断当前类是否是类c的子类。类的事,没有接口的事。
IsAbstract,判断是否为抽象的,含接口
关于Assembly的介绍:
加载程序集方法:
Assembly asm=Assembly.LoadFile(“c:\abc.dll”);
调用Assembly的GetExportedTypes方法可以得到Assembly中定义的所有的public类型
调用Assembly的GetTypes()方法可以得到Assembly中定义的所有的类型
调用Assembly的GetType(name)方法可以得到Assembly中定义的全名为name的类型信息
动态创建对象:
Activator.CreateInstance(Type t)会动态调用类的无参构造函数创建一个对象,返回值就是创建的对象,如果类没有无参构造函数就会报错
GetConstructor(参数列表);//这个是找到带参数的构造函数
动态调用的一些成员:
MemberInfo类 抽象类,有很多子类,下面讲的类都继承自它,获取程序集成员的相关信息(类型、方法、事件、字段和属性)
PropertyInfo 获取属性
主要成员:CanRead、CanWrite、PropertyType属性类型;SetValue、GetValue:读取值,设置值,第一个参数是实例对象,因为set、get要针对具体实例,最后一个参数null。 pInfo.SetValue(p1, 30, null)
MethodInfo 获取方法
MethodInfo都是和具体对象不相关的,所以需要第一个参数指定要执行的对象。
FieldInfo 获取字段
EventInfo 获取事件
反射若干场景(了解):
1.插件机制,保证了程序的封装性,同时提高了程序的可扩展性。
2.asp.net中根据用户访问的页面动态创建页面对象,比如访问Index.aspx,则根据访问的页面名称Index,通过反射动态创建该类型的对象。(在aspx页面中执行<%=System.Reflection.Assembly.GetExecutingAssembly().Location %>,查看dll中页面生成的的类名)
3.数据访问层中使用工厂模式时,根据配置文件动态创建数据访问层对象。
4.各种框架中,修改配置文件即可实现不同效果,都用到了反射。
5.访问类中的私有成员
6.各种需要动态创建类对象的场景都可以使用
隐式类型:
var关键字(在编译时已经能确定变量的类型了。并不是弱类型。)
var i = 5;//int
var j = 23.56;//double
var k = "C Sharp";//string
var x;//错误
var y = null;//错误
var z = { 1, 2, 3 };//错误 var z=new[] {1,2,3};//正确。
var 强类型.根据后面的值自动推断类型,编译时把推断的类型替换掉var
只是方便程序员使用而已。
不能作为类成员的类型、不能用作方法的参数,返回值。(只能用作局部变量的类型推断)
匿名类型:
var p1 = new { Id = 1, Name = "YJingLee", Age = 22 };//属性也不需要申明
var p2 = new { Id = 2, Name = "XieQing", Age = 25 };
数组
var intArray = new[] { 2, 3, 5, 6 };
var strArray = new[] { "Hello", "World" };
var objArray= new[] { new { Name = "YJingLee", Age = 22 }, new { Name = "XieQing", Age = 25 } };
扩展方法:
将扩展方法放到一个单独的类中,类声明为static(静态类),方法是静态方法,并且在第一个string上增加this。哇,string类“增加”扩展方法方法了,这只是假象,本质上仍然是静态方法,只不过C#提供了这样一个便利给大家,扩展方法内部不能调用被扩展类的私有、protected的东西。
声明扩展方法的步骤:类必须是static,方法是static,第一个参数是被扩展的对象,前面标注this(this 数据类型 参数名)。使用扩展方法的时候必须保证扩展方法类已经在当前代码中using
初始化器:
对象初始化器
Student stu= new Student(){ Id = 1, Name = "YJingLee", Age = 22 };
更方便给对象赋值,在构造函数之后执行赋值,看IL代码。
集合初始化器
List<int> num = new List<int> (){ 0, 1, 2, 6, 7, 8, 9 };
List< Student > stus= new List< Student >
{ new Student{Id=1,Name="YJingLee",Age=22},
new Student{Id=2,Name="XieQing",Age=25}, };
XML(可扩展标记语言)
Xml语法特点:
- 严格区分大小写
- 有且只能有一个根节点
- 有开始标签必须有结束标签
- 属性必须使用双引号
- 没有预定义标签与html不一样
- 文档声明:<?xml version="1.0" encoding="utf-8"?>
- 注释:<!-- -->
- CDATA区:即原意文本-<![CDATA[…]]>
- 注意编码问题,文本文件实际编码要与文档声明中的编码一致。
Xml读写(Dom XmlDocumnet demo):
l 两种方式,推荐使用第二种:
n XmlDocument
n XDocument、(XElement、XAttribute)
XmlDocument xdoc=new XmlDocument();
//文档声明
xdoc.AppendChild(xdoc.CreateXmlDeclaration("1.0","utf-8",null));
//添加根节点
XmlElement xeroot=xdoc.CreateElement("Order");
xdoc.AppendChild(xeroot);
XmlElement xeCust =xdoc.CreateElment("CustomerName");
xeCust.InnerText="老杨";
xeroot.AppendChild(xeCust);
XmlElement XeOrderName=xdoc.CreateElment("OrderName");
xeorderName.InnerText ="bj200000";
Xeroot.AppendChild(xeOrderName);
XmlElement xeItem=xdoc.CreateElement("Items");
xeroot.AppendChild(xeItem);
XmlElement order1=xdoc.CreateElement("OrderItem");
order1.SetAttribute("Name","电脑");
order1.SetAttribute("Count",30);
Xpath简介:
常用语法:
/AAA 选择根元素AAA
/AAA/CCC 选择AAA元素下的所有CCC子元素
/AAA/BBB/CCC 选择AAA元素下子元素BBB下所有CCC字元素
//BBB 选择所有BBB子元素
//* 选择所有元素
/AAA/BBB[1] 选择AAA下第一个BBB子元素
//@id 选择所有有id属性的元素
//BBB[@id] 选择有id属性的BBB元素
//BBB[NOT(@*)] 选择没有属性的BBB元素
//BBB[@id=’b1’] 选择id为b1的BBB元素
//*[COUNT(BBB)=2] 选择含有两个BBB子元素的元素
等等…….. 用的时候可以查找
深拷贝与浅拷贝
深拷贝是把所有的数据都拷贝一份,包括引用都会拷贝,而浅拷贝只是把值类型拷贝一份,而引用类型则没有。
图例:
浅拷贝的实现(demo):
/// <summary> /// 实现对象的浅拷贝 /// </summary> /// <returns></returns> public Person QianKaoBei() { //MemberwiseClone()方法从object类中继承下来 //作用就是把当前对象浅拷贝一份 //浅拷贝当前对象 return (Person)this.MemberwiseClone(); } |
深拷贝(序列化实现):
static object DeepCopy(object src) { BinaryFormatter Formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone)); using (MemoryStream stream = new MemoryStream()) { Formatter.Serialize(stream, src); stream.Position = 0; return Formatter.Deserialize(stream); } } |
Attribute特性(attribute)
是被指定给某一声明的一则附加的声明性信息
就是一个标签,在使用当前类的地方,通过反射获取该特性
Attribute就是类,Attribute类名一般都以Attribute结尾,使用的时候不用加Attribute这个结尾。使用Attribute的时候给的参数其实就是构造函数
反编译DisplaynameAttribute,发现没有特殊代码。标签本身是没有含义,只是起到标注的作用,具体怎么解释、使用这个标识,是由使用者来决定:属性视图窗口、C#编译器。相当于在放上画一个“拆”,本身这个字没有含义。
自定义特性(核心 反射):
Attribute都从System. Attribute类继承,类名一般以Attribute结尾
创建构造函数
标记类的用途—AttributeUsage标记(标记的标记):AttributeTargets:可以添加到哪些成员上。AllowMultiple:是否允许在一个成员上标记这个Attribute的多个实例,默认false。标注在Attribute类的类名上方。
Attribute只是给类、成员做标记,本身没有什么意义,不会发生任何动作,就像小偷踩点在门上做标记(StealableAttribute)一样,具体这个标记会起到什么作用是由使用这个类、成员的代码决定的。
Property.GetCustomerAttribute()