C#高级编程学习笔记(二)

31.比较值类型的相等性 
ReferenceEquals()用于比较引用,Equals()用于比较值。比较运算符可以看作一个中间项,但最大的区别是值类型需要装箱才能把它转化为引用。

32.比较运算符重载
语句if(a==b)对于类,这个语句在默认状态下会比较引用a和b,检测这两个引用是否指向内存中的同一个地址,而不是检测两个实例实际上是否包含相同的数据。
C#要求成对的重载运算符,比较运算符必须返回bool类型的值。

33.比较运算符:浅度比较与深度比较
如果有嵌入的类,浅度比较:比较引用是否指向同一个对象  深度比较:比较对象的值是否相等

34.基类和派生类之间的类型强制转换
在进行类型强制转换时,会检查被引用的对象,因为基类引用原则上可以引用一个派生类的实例,如果该对象不是派生类的一个实例,强制转换就会失败。

35.委托 
委托是类型安全的类,它定义了返回类型和参数的类型,委托类不仅包含对方法的引用,也可以包含对多个方法的引用。
当参数是委托类型时,就可以使用lambda表达式实现委托引用的方法。
当要把方法传送给其他方法时,需要使用委托。

36.声明委托
使用委托时,首先必须定义要使用的委托,必须创建该委托的一个或多个实例。定义的前面加上关键字delegate,

37.委托推断
为了减少输入量,只要需要委托实例,就可以只传送地址的名称。委托推断可以在需要委托实例的任何地方使用,也可以用于事件,因为事件基于委托。

38.泛型Action<T>委托表示引用一个void返回类型的方法,这个委托存在不同的变体,可以传递至多16种不同的参数类型。没有泛型参数的Action类可以调用没有参数的方法。

39.多播委托
如果要调用多个方法,就需要多次显式调用这个委托。但是,委托也可以包含多个方法。这种委托称为多播委托。如果调用多播 委托,就可以按顺序连续调用多个方法。为此,委托的签名就必须返回void

40.GetInvocationList()
为了避免因为前面方法抛出异常,导致迭代停止,可以自己迭代方法列表。delegate类定义GetInvocationList()方法,它返回一个Delegate对象数组。使用这个委托调用捕获异常,继续下一次迭代。

41.闭包和lambda表达式
通过lambda表达式可以访问lambda表达式块外部的变量,称为闭包。
若要创建 Lambda 表达式,需要在 Lambda 运算符 => 左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块。 例如,lambda 表达式 x => x * x 指定名为 x 的参数并返回 x 的平方值。 如上面的示例所示,你可以将此表达式分配给委托类型:
"Lambda表达式"是一个特殊的匿名函数,是一种高效的类似于函数式编程的表达式,Lambda简化了开发中需要编写的代码量。它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型,支持带有可绑定到委托或表达式树的输入参数的内联表达式。所有Lambda表达式都使用Lambda运算符=>,该运算符读作"goes to"。Lambda运算符的左边是输入参数(如果有),右边是表达式或语句块。Lambda表达式x => x * x读作"x goes to x times x"。

42.事件
事件基于委托,为委托提供了一种发布/订阅机制。在架构内到处都能看到事件,在window应用程序中,button类提供了click事件,这类事件就是委托。

43.弱事件
通过事件,直接连接到发布程序和侦听器。但垃圾回收有个一个问题,垃圾回收器不能清空侦听器占用的内存,这种强连接可以通过弱事件模式来解决,使用WeakEventManager作为发布程序和侦听器之间的中介。

44.StringBuilder类
可以进行的处理仅限于替换和追加或删除字符串中的文本,但是工作方式非常的高效。
两个重要的属性:
length指定字符串的实际长度
Capacity指定字符串在分配的内存中的最大长度
命名空间:System.Text
在只是连接两个字符串时 string类性能会更好。

45.正则表达式
是一种专门用来处理字符串的语言。

System.Text.RegularExpressions名称空间中Regex类的静态方法Matches()
这个方法的参数是一些输入文本,一个模式和RegexOptions枚举中的一组可选标志。
在正则表达式模式中,也可以把任何字符组合起来(包括元字符和转义序列)

46.集合排序
List<T>类可以使用Sort()方法对元素排序。sort()方法使用快速排序算法,比较所有元素,直到整个列表排好序为止。

47.队列
队列是元素以先进先出的方式来处理的集合。
队列使用System.Collections.Generic名称空间中的泛型类Queue<T>实现

Queue<T>类没有实现IList<T>接口,所以不能用索引器访问队列,队列的元素会添加在队列的尾部

48.栈
最后添加到栈中的元素会最先读取,栈是一个后进先出的容器
用Push()方法在栈中添加元素,用Pop()方法获取最近添加的元素。
Stack<T>类实现IEnumerable<T>和ICollection接口。
peek()返回栈顶的元素但不删除它。

49.链表 
LinkedList<T>是一个双向链表,其元素指向它前面和后面的元素。在插入一个元素时,只需要修改上一个元素的Next引用和下一个元素的Previous引用,使它们引用所插入的元素。
在List<T>类中,插入一个元素时,需要移动该元素后面的所有元素。

50.有序列表
SortedList<TKey,TValue>类,这个类按照键给元素排序,这个集合中的值和键都可以使用任意类型

51.字典
表示一种非常复杂的数据结构,这种数据结构允许按照某个键来访问元素,字典也称为映射或散列表。字典的主要特征是能根据键快速查找值,也可以自由添加和删除元素,没有在内存中移动后续元素的性能开销。
主要类是Dictionary<TKey,TValue> 

用作字典中键的类型必须重写Object类的GetHashCode()方法。只要字典类需要确定元素的位置,它就要调用GetHashCode()方法

字典的容量是一个素数解答:
字典的内部实现方法。
字典在内部是通过hash表来实现查找的,而查找树在数据结构上采用了有序二叉树与“哈希桶”的一种数据结构来管理元素。
在每个哈希桶里,为了能快速找到一个元素是否存在于此桶,需要为每一个元素进行哈希编码,哈希编码常常采用除余法,也就是说,对于一个给定的a值, 其哈希码h = a mod p, 当这个p是一个素数时,h的冲突机率最少,查找速度也就最快。试想,如果是一个偶数的话,就会有更多情况下的余数冲突。
如果能将a保存到一个桶里的h值对应的下标的话,就可以用h直接定位到元素a,因此,就要求某个桶的容量是p。
这就是为何哈希桶的大小必须是一个素数的原因。
如果你给的构造参数不是素数,构造函数后向后找到大于等于你提供的初始容量的第一个素数,做为其第一个桶的大小。 

52.有序字典
SortedDictionary<TKey,TValue>类是以一个二叉搜索树,其中的元素根据键来排序,该键类型必须实现IComparable<TKey>接口。如果键的类型不能排序,还可以创建一个实现了IComparer<TKey>接口的比较器。

53.集
包含不重复元素的集合称为“集(set)”。.NET Framework包含两个集HashSet<T>和SortedSet<T>,它们都实现ISet<T>接口。HashSet<T>集包含不重复元素的无序列表,SortedSet<T>集包含不重复元素的有序列表。
ISet<T>接口提供的方法可以创建合集,交集,或者给出一个是另一个集的超集或子集的信息。

54.异或
异或是一个数学运算符。他应用于逻辑运算。 
例如:真异或假的结果是真,假异或真的结果也是真,真异或真的结果是假,假异或假的结果是假。就是说两个值相 异结果为真。 
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0

55.LINQ
语言集成查询:在C#语言中集成了查询语法,可以用相同的语法访问不同的数据源。LINQ提供了不同数据源的抽象层,所以可以使用相同的语法。

56.LINQ查询
使用这些准备好的列表和实体,进行查询 子句from where orderby descending 和select 都是这个查询中预定义的关键字。

57.扩展方法
可以将方法写入最初没有提供该方法的类中,还可以把方法添加到实现某个特定接口的任何类中,这样多个类就可以使用相同的实现代码。
扩展方法定义为静态方法,第一个参数定义了它扩展的类型。

58.CLR IL 含义
CLR common language runtime 就是运行时相当于一个运行环境(相当于JVM)
IL intermidiate language中间语言,C#先编译成中间语言再在CLR上面执行 

.NET运行时任何有意义的操作都是在堆栈上完成的,而不是直接操作寄存器。这就为.NET跨平台打下了基础,通过设计不同的编译器编译相同的IL代码来实现跨平台。对于堆栈我们的操作无非就是压栈和出栈,在IL中压栈通常以ld开头,出栈则以st开头。知道这个后再看上面的指令感觉一下子就豁然开朗了,接下来继续学习的步伐,下面的表格是对于一些常见ld指令。st指令则是将ld指令换成st,功能有压栈变为出栈,有时候会看到在st或ld后加.s这表示只取一个字节。再来看看流程控制,知道压出栈和流程控制后,基本上看出IL的大概意思那就冒闷踢啦。流程控制主要就是循环和分支,下面我写了个有循环和分支的小程序。其中我们用到了加法和比较运算,为此得在这里介绍最基本的三种运算:算术运算(add、sub、mul乘法、div、rem求余);比较运算(cgt大于、clt小于、ceq等于);位运算(not、and、or、xor异或、左移shl、右移shr)。要注意在比较运算中,当执行完指令后会直接将结果1或0压栈,这个过程是自动完成的。对于流程控制,主要是br、brture和brfalse这3条指令,其中br是直接进行跳转,brture和brture则是进行判断再进行跳转。
ldarg
加载成员的参数,如ldarg.0
ldarga
装载参数的地址,注意一般加个a表示取地址
ldc
将数字常量压栈,如ldc.i4.2
ldstr
将字符串的引用压栈
ldloc/ldloca
ldloc将一个局部变量压栈,加a表示将这个局部变量的地址压栈
Ldelem
表示将数组元素压栈
ldlen
将数组长度压栈
ldind
将地址压栈,以地址来访问或操作数据内

59.ScriptRuntime
可以执行存储在文件中的代码段或完整的脚本,可以选择合适的语言引擎,或者让DLR确定使用什么引擎。脚本可以在自己的应用程序域中创建,不仅可以给脚本传入数值并从脚本中传出数值,还可以在脚本中调用在动态对象上创建的方法。
启动ScriptRuntime环境的4个特定步骤:
创建ScriptRuntime对象,设置合适的ScriptEngine,创建ScriptSource,创建ScriptScope。
ScriptRuntime: 创建 DLR 运行环境,这是整个执行过程的起始点,它表示一个全局的执行状态(比如程序集引用等等)。每个应用程序域(AppDomain)中可以启动多个 ScriptRuntime。
ScriptScope: 构建一个执行上下文,其中保存了环境及全局变量。宿主(Host)可以通过创建不同的 ScriptScope 来提供多个数据隔离的执行上下文。
ScriptEngine: DLR 动态语言(比如 IronPython) 执行类,可于解析和执行动态语言代码。
ScriptSource: 操控动态语言代码的类型,我们可以编译(Compile)、读取(Read Code Lines)或运行(Execute)代码。
CompiledCode: 调用 ScriptSource.Compile() 将源代码编译成 CompiledCode,这样多次执行就无需重复编译,从而提高执行性能。
ObjectOperations: 提供了相关方法,允许我们在宿主(Host)中操作 DLR 对象成员(Member)。
该对象使用CreateFromConfiguration()静态方法创建。


60.语法糖
语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

实际上从面向过程到面向对象也是一种语法糖,C语言可以通过它的指针、类型转换,结构体实现面向对象的编程风格,但是C++更进一步的推广了这种风格,更加易用,不过到了C#把OO的风格发挥得淋漓尽致。OO的编程风格对于面向过程来说是不是一种语法糖呢?如果生硬地照此理解,只有计算机硬件指令才不算语法糖,而其他一切利用编译器、汇编器将代码抽象,和自然语言更相近的手段都算语法糖

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页