C#复习笔记(2)--C#1所搭建的核心基础
通过对C#1所搭建的核心基础的深入了解,可以知道之后的C#版本在C#1的基础上做了很多扩展,而这些扩展都是基于C#搭建的核心基础而来的。
委托
一、编写委托的过程
委托经常和C语言的“函数指针”挂钩。委托是方法参数化、函数式语言一个重要的表达方式。C#1中编写一个委托要经过四部:
1、声明委托类型
delegate void StringProcessor(string param1);
这个委托指定了一种无返回值,有一个string类型的参数的方法。
这个委托继承自System.MulticastDelegate,后者又派生自System.Delegate.
委托本身是引用类型,所以声明委托的时候不能在方法中声明。可以作为内部类,也可以声明到namaspace下面。
2.、必须有一个方法包含了要执行的代码,这个方法要和委托声明的签名一致,包括参数和返回值
C#一中要为委托找到一个方法的话必须要和委托的签名完全一直,在C#2中允许委托的斜边和逆变。必须下面的委托:
delegate object SayHello(string word);
这个委托可以被下面这个方法实例化:
static string Sayhello(object word)
{
Console.WriteLine(word);
return word.ToString();
}
3、创建委托本身
可以采用new操作符来创建一个委托的实例:
SayHello say = new SayHello(Sayhello);
C#2中支持委托-方法组的转换,所以,可以直接使用
SayHello say = Sayhello;
4、调用委托
一切准备就绪后就可以用invoke方法来完成调用。C#还可以更简单的完成这个操作,使用
SayHello say = Sayhello;
say("hello,you");
就可以完成,不过在背后编译器帮你完成了一些工作:
二、合并和删除委托
委托和string的特征有一些相似:都是不易变的。这体现在委托的合并和删除上面:
委托内部有一个操作列表(invocation list):System.Delegate的静态方法Combine和Remove负责创建新的委托实例。其中,Combine将两个委托的操作列表链接到一起,Remove负责将一个委托实例的一个操作删除。它们都不改变原有的类型(所以说和string很像)他们都是返回一个Delegate。
很少在代码中直接调用Delegate.Combine,Delegate.Remove而是用+=和-=操作符。这同样是编译器的功劳:
如果调用列表中抛出异常,那么抛出异常的那个方法导致调用列表不在执行。
如果有返回值,那么调用列表会返回最后调用的那个方法的值。所以一般不会在调用列表中执行有返回值的方法。
但是如果有必要,可以通过调用invocationlist来逐个执行方法并获取返回值。
三、事件
首先来看事件的声明:
public event SayHello SayHelloEvent;
事件是对委托的封装,有点儿类似于属性和后备字段的关系,上面这句声明编译器会做如下工作:
1、在相同的作用域中声明一个SayHello委托类型的私有字段
2、声明一个类似于属性的块结构,这个块结构包含一个类似于属性的取值方法和赋值方法,由编译器进行命名,前缀分别是"add"和"remove"。
3、在外部,会通过+=和-=来操作事件,声明事件时的访问修饰符会限制这一操作。就是说如果声明为一个private的事件的话外部是不能操作事件的。
4、在内部,+=操作符会调用“add”前缀的方法,“add”前缀的方法会调用Delegate.Combine来合并操作,同理,-=操作符会调用“remove”前缀的方法,该方法会调用Delegate.Remove来删除操作。
5、从上面可以看出,事件就是一对儿“add"和”remove“方法。他封装了委托,从而避免调用方直接操作委托,而是通过事件来间接的操作委托,在类的内部可以看到委托,在类外部可以看见事件。
总结一下,事件不是委托实例,只是成对儿出现的add/remove方法。类似与属性的get/set方法。
类型系统的特征
在C#4之前,C#的类型系统是静态的、显式的和安全的。
一、静态类型和动态类型
C#是静态类型的:每个变量或表达式的类型在编译时都是已知的。只有类型已知的操作才是被允许的。静态这个词用来表示使用不变的类型数据来分析哪些操作可用。
与静态类型想对应的是动态类型,动态类型的实质是变量中含有值,但那些值不限于特定的类型。所以编译器不能执行相同形式的检查。
二、显示类型和隐式类型
这个讨论只有在静态语言的环境中才是成立的。对于显式类型来说,每个变量都在声明时指定类型。隐式类型则允许编译器变量的用途来确定变量的类型。无论是显式还是隐式,表达式的类型都会在编译时就确定。
三、类型安全与类型不安全
C#是类型安全的,C#支持一些类型安全的转换:继承上的、数值上的,如果使用强制类型转换,编译器会检测转换的结果,也会抛出异常来组织程序的继续运行,C#还支持有限的协变和逆变(C#4)。但是和真正的协变和逆变还有很长一段距离。
可以通过显式实现接口来处理协变和逆变上的一些限制。
总结一下:
C# 1是静态类型的—— 编译器知道你能使用哪些成员;
C# 1是显式的—— 必须告诉编译器变量具有什么类型;
C# 1是安全的——除非存在真实的转换关系, 否则不能将一种类型当做另一种类型;
静态类型仍然不允许一个集合成为强类型的“ 字符串列表” 或者“ 整数列表”, 除非针对不同的元素使用大量的重复代码;
方法覆盖和接口实现不允许协变性/ 逆变性。
值类型和引用类型
一、值类型和引用类型的基础知识
对于引用类型的表达式(如一个变量),它的值是一个引用,而非对象。
引用就像URL——是允许你访问真实信息的一小片数据。
对于值类型的表达式,它的值是实际的数据。
有时,值类型比引用类型更有效, 有时恰好相反。
引用类型的对象总是在堆 上, 值类型的值既可能在栈 上, 也可能在堆上,具体取决于上下文。
引用类型作为方法参数使用时, 参数默认是以“值传递”方式来传递的—— 但值本身是一个引用。
值类型和引用类型的本质区别在于复制的方式不同:值类型复制值本身,引用类型复制的是引用。
两种类型的另一个差异在于, 值类型不可以派生出其他类型。 这将导致的一个结果就是,值不需要额外的信息来描述值实际是什么类型。 把它同引用类型比较, 对于引用类型来说, 每个对象的开头都包含一个数据块, 它标识了对象的实际类型, 同时还提供了其他 一些信息。 永远都不能改变对象的类型——执行简单的强制类型转换时, 运行时会获取一个引用, 检查它引用的对象是不是目标类型的一个有效对象。如果有效, 就返回原始引用; 否则抛出异常。 引用本身并不知道对象的类型—— 所以同一个引用“ 值” 可用于( 引用) 不同类型的多个变量。 例如 下面 的 代码:
Stream stream=new MemoryStream();
MemoryStream memory = (MemoryStream) stream;
//第1行创建一个新的MemoryStream对象, 并将stream变量的值设为对那个新对象的引用。 第2行检查stream的值引用的是不是一个MemoryStream( 或派生类型)对象,并将MemoryStream的值设为相同的值。
值类型变量本身存储的是值,引用类型变量本身存储的是引用,这个引用里面包含一个指向真正对象的地址。
变量的值在它声明时的位置存储。局部变量的值总是存储在栈( stack)中(这个结论只有 在C#1中完全成立。以后会讲到,在更高版本C#中,在特定情况下,局部变量最终可能存储在堆中。)实例变量的值总是存储在实例本身存储的地方。 引用类型实例(对象)总是存储在堆( heap)中, 静态变量也是。
按值传递和按引用传递的区别是按值传递的是副本,按引用传递的是别名。
装箱的背景在于值类型和引用类型的值不同:值类型的值就是值本身,而引用类型的值只是一个引用。
值类型的值会在需要引用类型的行为时被装箱; 拆箱则是相反的过程。
构建于C#1之上的新特性
一、与委托有关的新特性
C#1中的委托-->C#2的泛型、方法组转换、匿名方法、委托协变性和逆变性-->C#3的lambda表达式、内建的Func、Action、泛型的接口和委托的协变性和逆变性
二、与类型系统有关的特性
C#2-->泛型、委托的协变性和逆变性-->C#3匿名类型、隐式类型、扩展方法-->C#4受限的泛型协变性和逆变性、动态类型
三、与值类型有关的特性
C#2-->泛型、Nullable<t>可空值类型
C#复习笔记(2)--C#1所搭建的核心基础的更多相关文章
- c#1所搭建的核心基础之值类型和引用类型
这个主题很重要,在.NET中做的一切其实都是在和一个值类型或者引用类型打交道. 现实世界中的值和引用 假定你在读一份非常棒的东西,希望一个朋友也去读他.于是你到复印室里复印了一份.这个时候他获得了属于 ...
- c#1所搭建的核心基础之类型系统的特征
类型系统的特征简介 几乎每种编程语言都有某种形式的一个类型系统.类型系统大致被分为:强/弱,安全/不安全,静态/动态,显式/隐式等类型. c#在类型系统世界中的位置 c#1的类型系统是静态的.显式的和 ...
- c#1所搭建的核心基础之委托
本文将对c#1的委托进行详细探索 委托(delegate) 注 delegate:vt.委派代表; 授权给; [法律]债务转移; 委托作用:在恰当的时间执行一系列操作 1.简单委托的构成 声明委 ...
- C#1所搭建的核心基础
一,委托 委托封装了包含特殊返回类型和一组参数的行为,类似包含单一方法接口. 委托类型声明中所描述的类型签名决定了哪个方法可以用于创建委托实例,同时决定了调用的签名:委托类型实际上只是参数类型的一个列 ...
- Java基础复习笔记系列 九 网络编程
Java基础复习笔记系列之 网络编程 学习资料参考: 1.http://www.icoolxue.com/ 2. 1.网络编程的基础概念. TCP/IP协议:Socket编程:IP地址. 中国和美国之 ...
- Java基础复习笔记系列 八 多线程编程
Java基础复习笔记系列之 多线程编程 参考地址: http://blog.csdn.net/xuweilinjijis/article/details/8878649 今天的故事,让我们从上面这个图 ...
- Java基础复习笔记系列 七 IO操作
Java基础复习笔记系列之 IO操作 我们说的出入,都是站在程序的角度来说的.FileInputStream是读入数据.?????? 1.流是什么东西? 这章的理解的关键是:形象思维.一个管道插入了一 ...
- Java基础复习笔记系列 五 常用类
Java基础复习笔记系列之 常用类 1.String类介绍. 首先看类所属的包:java.lang.String类. 再看它的构造方法: 2. String s1 = “hello”: String ...
- Java基础复习笔记系列 四 数组
Java基础复习笔记系列之 数组 1.数组初步介绍? Java中的数组是引用类型,不可以直接分配在栈上.不同于C(在Java中,除了基础数据类型外,所有的类型都是引用类型.) Java中的数组在申明时 ...
随机推荐
- PTA天梯 L3-007 天梯地图
L3-007 天梯地图 题目: 本题要求你实现一个天梯赛专属在线地图,队员输入自己学校所在地和赛场地点后,该地图应该推荐两条路线:一条是最快到达路线:一条是最短距离的路线.题目保证对任意的查询请求,地 ...
- P1433 吃奶酪(搜索DFS+记忆化)
emmmmm,我还是看了题解的....尴尬,其实不用记忆化搜索也是可以的.因为我不用也是最后一个点超时.但是我是用的贪心+DFS...超时的原因是贪心....mmp,本来加贪心就是为了不超时.... ...
- 【angularJS】过滤器
1.分类: <1>内置过滤器(见4) <2>自定义过滤器 2.作用:接收一个输入,通过某个规则进行处理,然后返回处理后的结果 3.应用: <1>在模板中使用 用法 ...
- 【PHP】最详细PHP从入门到精通(一)——想学习PHP的朋友们福利来了!
PHP从入门到精通 (一)PHP简介和基本知识 PHP(外文名:PHP: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言.语法吸收了C语言.Java ...
- 猜数字游戏,判断输入的数字与系统产生的数字是否一致(Math.random()与if嵌套循环)
package com.summer.cn; import java.util.Scanner; public class Test041509 { /** * java 随机数 Math * Mat ...
- WebApi测试工具:SAEA.RESTED
写好一个Api接口不知道怎么测试?试试SAEA.RESTED吧——无需任何代码.不污染主项目.快速上手.不依赖IIS,可供多人共享使用!下面就跟着本文查看如何使用吧:1.下载安装:https://gi ...
- Nginx+IIS简单的部署
随着互联网项目用户访问量不断上升,单点web服务器是无法满足大型高并发高负载的业务处理的,为了给web服务器做负载均衡方案,打算采用Nginx搭建负载均衡服务器,把用户请求分配到N个服务器来缓解服务器 ...
- 9宫拼图小游戏(WPF MVVM实现)
昨天逛论坛,看到一个哥们用WPF做了一个9宫的拼图游戏,发现初学WPF的人都很容易犯一个错误(我也犯过):把WPF当WINFORM用!所以想写一个比较符合WPF风格的版本,于是就抽工作的空余时间做了一 ...
- VS2017开发的IDE扩展
Tag Helpers 智能提示 Razor Language Services: https://marketplace.visualstudio.com/items?itemName=ms-mad ...
- Flask的蓝图和红图
1.蓝图 对于简单的项目来说,比如项目就只有一个user模块,我们可以都将视图函数定义在一个文件里面,不需要用到蓝图. 但是如果我们的项目有多个模块,如下有v1模块,v2模块.....等,那么如果我们 ...