通过对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所搭建的核心基础的更多相关文章

  1. c#1所搭建的核心基础之值类型和引用类型

    这个主题很重要,在.NET中做的一切其实都是在和一个值类型或者引用类型打交道. 现实世界中的值和引用 假定你在读一份非常棒的东西,希望一个朋友也去读他.于是你到复印室里复印了一份.这个时候他获得了属于 ...

  2. c#1所搭建的核心基础之类型系统的特征

    类型系统的特征简介 几乎每种编程语言都有某种形式的一个类型系统.类型系统大致被分为:强/弱,安全/不安全,静态/动态,显式/隐式等类型. c#在类型系统世界中的位置 c#1的类型系统是静态的.显式的和 ...

  3. c#1所搭建的核心基础之委托

    本文将对c#1的委托进行详细探索 委托(delegate)   注 delegate:vt.委派代表; 授权给; [法律]债务转移;  委托作用:在恰当的时间执行一系列操作 1.简单委托的构成 声明委 ...

  4. C#1所搭建的核心基础

    一,委托 委托封装了包含特殊返回类型和一组参数的行为,类似包含单一方法接口. 委托类型声明中所描述的类型签名决定了哪个方法可以用于创建委托实例,同时决定了调用的签名:委托类型实际上只是参数类型的一个列 ...

  5. Java基础复习笔记系列 九 网络编程

    Java基础复习笔记系列之 网络编程 学习资料参考: 1.http://www.icoolxue.com/ 2. 1.网络编程的基础概念. TCP/IP协议:Socket编程:IP地址. 中国和美国之 ...

  6. Java基础复习笔记系列 八 多线程编程

    Java基础复习笔记系列之 多线程编程 参考地址: http://blog.csdn.net/xuweilinjijis/article/details/8878649 今天的故事,让我们从上面这个图 ...

  7. Java基础复习笔记系列 七 IO操作

    Java基础复习笔记系列之 IO操作 我们说的出入,都是站在程序的角度来说的.FileInputStream是读入数据.?????? 1.流是什么东西? 这章的理解的关键是:形象思维.一个管道插入了一 ...

  8. Java基础复习笔记系列 五 常用类

    Java基础复习笔记系列之 常用类 1.String类介绍. 首先看类所属的包:java.lang.String类. 再看它的构造方法: 2. String s1 = “hello”: String ...

  9. Java基础复习笔记系列 四 数组

    Java基础复习笔记系列之 数组 1.数组初步介绍? Java中的数组是引用类型,不可以直接分配在栈上.不同于C(在Java中,除了基础数据类型外,所有的类型都是引用类型.) Java中的数组在申明时 ...

随机推荐

  1. MYSQL的binlog日志

    binlog 基本认识 MySQL的二进制日志以事件形式,记录了所有的DDL和DML(除了数据查询语句)语句,及语句执行消耗时间. MySQL的二进制日志是事务安全型的,是MySQL最重要的日志. b ...

  2. Spark Streaming和Kafka整合保证数据零丢失

    当我们正确地部署好Spark Streaming,我们就可以使用Spark Streaming提供的零数据丢失机制.为了体验这个关键的特性,你需要满足以下几个先决条件: 1.输入的数据来自可靠的数据源 ...

  3. Gulp的简单使用

    我比较喜欢使用Gulp,因为简单好用! 今天的任务是:使用Gulp来压缩 jQuery源码,各输出一个压缩的和未压缩的版本 第一步:安装 cnpm install gulp --save-dev cn ...

  4. java中伪共享问题

    伪共享(False Sharing) 原文地址:http://ifeve.com/false-sharing/ 作者:Martin Thompson  译者:丁一 缓存系统中是以缓存行(cache l ...

  5. 使用vue-cli脚手架创建项目

    ue-cli 是一个官方发布 vue.js 项目脚手架,使用 vue-cli 可以快速创建 vue 项目. GitHub地址是:https://github.com/vuejs/vue-cli 一.安 ...

  6. 6.05-btc

    import requests from lxml import etree import json class BtcSpider(object): def __init__(self): self ...

  7. 1.02-get-params

    import urllib.request import urllib.parse import string def get_method_params(): url = "http:// ...

  8. jvm的解释执行与编译执行

    1.原理 字节码无法直接交给硬件执行需要虚拟机翻译成机器码才能执行,“翻译”的策略有两种:解释执行和编译执行又称即使编译(JIT).解释执行是没执行一句字节码的时候把字节码翻译成机器码并执行,优点是启 ...

  9. Python:Day26 socket

    SOCKET通信流程 服务器创建套接字链接: 1.创建SOCKET,socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=No ...

  10. 【vue】钩子函数生命周期

    图1 图2: 图3 相关资料:http://www.zhimengzhe.com/Javascriptjiaocheng/236707.html    https://segmentfault.com ...