C基础知识需要特别注意的知识点.docx

上传人:b****1 文档编号:20148217 上传时间:2023-04-25 格式:DOCX 页数:27 大小:29.04KB
下载 相关 举报
C基础知识需要特别注意的知识点.docx_第1页
第1页 / 共27页
C基础知识需要特别注意的知识点.docx_第2页
第2页 / 共27页
C基础知识需要特别注意的知识点.docx_第3页
第3页 / 共27页
C基础知识需要特别注意的知识点.docx_第4页
第4页 / 共27页
C基础知识需要特别注意的知识点.docx_第5页
第5页 / 共27页
点击查看更多>>
下载资源
资源描述

C基础知识需要特别注意的知识点.docx

《C基础知识需要特别注意的知识点.docx》由会员分享,可在线阅读,更多相关《C基础知识需要特别注意的知识点.docx(27页珍藏版)》请在冰豆网上搜索。

C基础知识需要特别注意的知识点.docx

C基础知识需要特别注意的知识点

《C#4.0本质论》学习笔记

一、值类型和引用类型的区别

值类型

引用类型

1.直接包含值,变量引用的位置就是值在内存中实际存储的位置。

存储是对一个内存位置的引用(内存地址),要去那个位置才能找到真正的数据

2.数据存储在栈的内存区域中

数据存储在堆的内存区域

3.在编译时确定内存量

在运行时,从变量中读取内存地址,然后到指定内存地址中读取数据。

4.复制数据的一个副本

只复制数据的地址

5.将new用于值类型,会使用默认值初始化内存

new,调用构造函数生成一个对象(实例)

二、装箱与拆箱

1.装箱:

将一个值类型转换成一个引用类型

1)首先在堆中分配好内存;

2)一次内存复制:

栈上的值类型数据复制到堆上分配好的位置;

3)对象或接口引用得到更新,指向堆上的位置。

2.拆箱:

将一个引用类型转换为值类型

3.装箱频繁发生,会大幅影响性能;

4.不允许在lock()语句中使用值类型。

三、String与StringBuilder

1.String对象称为不可变的(只读),因为一旦创建了该对象,就不能修改该对象的值

2.StringBuilder此类表示值为可变字符序列的类似字符串的对象

3.String对象串联操作总是用现有字符串和新数据创建新的对象。

StringBuilder对象维护一个缓冲区,以便容纳新数据的串联。

如果有足够的空间,新数据将被追加到缓冲区的末尾;否则,将分配一个新的、更大的缓冲区,原始缓冲区中的数据被复制到新的缓冲区,然后将新数据追加到新的缓冲区。

4.String或StringBuilder对象的串联操作的性能取决于内存分配的发生频率。

String串联操作每次都分配内存,而StringBuilder串联操作仅当StringBuilder对象缓冲区太小而无法容纳新数据时才分配内存。

因此,如果串联固定数量的String对象,则String类更适合串联操作。

这种情况下,编译器甚至会将各个串联操作组合到一个操作中。

如果串联任意数量的字符串,则StringBuilder对象更适合串联操作;例如,某个循环对用户输入的任意数量的字符串进行串联。

四、?

和?

?

的使用

1.可空修饰符?

为了声明可以存null的值类型变量。

int?

x=null;

2.使用?

?

运算符分配默认值:

expression1?

?

expression2.检查第一个表达式是否为null,如果为null,则返回第二个表达式。

3.当前值为空的可以为null的类型被赋值给非空类型时将应用该默认值,如int?

x=null;inty=x?

?

-1;。

三、const和readonly

1.const:

1)既然用于修饰字段,又可以修饰局部变量;

2)是在编译时确定的值,不可以在运行时改变;

3)自动成为静态字段,不能显式声明为static

2.readonly:

1)只能用于字段(不能用于局部变量),

2)指出字段值只能从构造函数中更改,或者直接在声明时指定。

——可以在运行时赋值;

3)readony字段既可以是实例字段,也可以是静态字段.

四、静态成员和实例成员

1.静态字段:

主要存储的是对于类的数据,能由多个实例共享,需要使用static关键字

2.实例字段:

存储的是与对象关联的数据,只能从类的一个实例(对象)中访问实例字段。

3.静态方法:

不能直接访问一个类中的实例字段,必须获取类的一个实例,才能调用任一实例成员(方法或字段)。

4.实例方法:

将需要访问实例数据的方法声明为实例方法。

5.静态构造函数:

用来对类(而不是类实例)进行初始化。

运行时会在“访问类的一个静态方法或者字段时”自动调用静态构造函数。

6.静态类:

不包含任何实例字段(或方法),声明时用static关键字。

不能被实例化,不能被继承。

五、继承:

对一个现有的类型进行扩展,以包含附加的成员或实现对基类成员的定制。

1.protected访问修饰符:

在基类中定义只有派生类才能访问的成员。

规则:

要从一个派生类中访问一个受保护的成员,必须在编译时确定受保护的成员是派生类(或者它的某个子类)的一个实例。

2.C#是一种单一继承的语言,一个类不能直接从两个类派生。

3.可以使用聚合解决多重继承的问题

4.使用sealed修饰符,实现密封类(不能被继承)

5.C#支持重写实例方法和属性,但不支持重写字段或者任何静态成员。

1)在基类中使用virtual修饰符标记每一个需要重写的成员,

2)在派生类中,用override进行修饰。

C#要求重写方法显式地使用override关键字。

3)重写一个成员时,会造成“运行时”调用派生得最远的实现。

4)new修饰符:

在基类面前隐藏了派生类的重新声明的成员。

6.抽象类:

1)仅供派生的类,不能被实例化。

2)包含抽象成员:

不具有实现的一个方法或属性,强制所有派生类提供实现。

7.多态性:

同一个签名可以有多个实现。

1)抽象成员是实现多态性的一个手段:

基类指定方法的签名,派生类提供具体的实现;

2)可以利用多态性:

调用基类的方法,当方法具体由派生类实现。

8.is和as运算符

1)is运算符验证基础类型

2)使用as运算符进行转换:

将对象转换为一个特定的数据类型,若源类型不是固有的目标类型,as运算符会将null值赋给目标。

六、接口

1.对接口的理解:

1)接口定义了一系列成员,不包含任何实现,由继承该接口的类实现;

2)接口实现关系是一个“能做”关系:

类“能做”接口要求的事情;

3)接口定义了一个“契约”:

实现接口的类会使用与被实现的接口相同的签名来定义方法。

4)接口的宗旨是:

定义由多个类共同遵守的一个契约,所有成员都自动定义为public。

5)C#不允许为接口成员使用访问修饰符。

6)通过接口可以实现多态性;

7)不能被实例化,不能使用new关键字来创建一个接口。

2.接口实现:

1)一个类只能从一个基类派生,但可以实现多个接口;

2)显式实现:

为了声明一个显式接口成员实现,需要在成员名之前附加接口名前缀。

String[]IListable.ColumnValues

{

…….

}

通过接口本身来调用它——将对象转型为接口;

Values=((IListable)contact1).Columnvalues

3)隐式实现:

类成员的签名与接口成员的签名相符。

调用时不需要转型,可以直接调用。

4)成员若是核心的类功能,则隐式实现;

5)假如一个成员的用途在实现类中不是很明确,就考虑使用一个显式的实现;

6)已经有一个同名的类成员,则可以使用显式实现。

3.接口继承:

1)一个接口可以从另一个接口派生,派生的接口将继承“基接口”的所有成员;

2)在用于显式接口成员实现的一个完全限定的接口成员名称中,必须引用最初声明它的那个接口的名称;

3)继承:

接口代表一份契约,而一份契约可指定另一份契约也必须遵守的条款。

4)通过接口可以实现多重继承。

4.接口与类的比较

抽象类

接口

仅供派生的类,不能被实例化

不能实例化

定义了基类必须实现的抽象成员签名

接口的所有成员要在基类中实现

可以包含存储在字段中的数据

不能存储任何数据。

只能在派生类中指定字段。

解决这个问题的办法,是在接口中定义属性,但不能包括实现。

扩展性比接口好,不会破坏任何版本兼容性。

在抽象类中,你可以添加附加的非抽象成员,它们可以由所有派生类继承

用更多的成员来扩展接口,会破坏版本兼容性。

(只能再创建一个接口,该接口可以从原始接口派生)

七、重写Equals()

1.“对象同一性”和“相等的对象值”

1)对象同一性:

两个引用,引用的是同一个实例;

2)相等的对象值:

两个引用,引用不同的实例,但两个对象实例值是相等的。

3)只有引用类型才可能引用相等,值类型不可能引用相等(ReferenceEquals()对值类型进行了装箱,由于每一个实参都被装到栈上的不同位置)。

2.实现Equals():

1)检查是否为null;

2)如果是引用类型,就检查引用是否相等;(ReferenceEquals()方法来判断引用是否相等)

3)检查数据类型是否相等;

4)调用一个指定了具体类型的辅助方法,它能将操作数视为要比较的类型,而不是一个对象;

5)可能要检查散列码是否相等。

(相等的两个对象,不可能散列码不同)

6)如果基类重写了Equals(),就检查base.Equals();

7)比较每一个标识字段,判断是否相等;

8)重写GetHashCode()。

9)重写==和!

=运算符。

八、垃圾回收与资源清理

1.垃圾回收内存

1)“运行时”的一个核心功能:

回收不再被引用的对象所占用的内存。

2)垃圾回收器只负责回收内存,不处理其他资源,比如数据库连接、句柄(文件、窗口等)、网络端口以及硬件设备(比如串口)等;

3)垃圾回收器处理的是引用对象,而且只回收堆上的内存;

2.终结器释放资源

1)终结器:

允许程序员编写代码来清理一个类的资源;

2)由垃圾回收器负责为一个对象实例调用终结器;

3)终结器会在上一次使用对象之后,并在应用程序关闭之前的某个时间运行。

4)终结器不允许传递任何参数,不能被重载,不能被显式调用,不允许使用访问修饰符;

5)终结器负责释放像数据库连接和文件句柄这样的资源,不负责回收内存(回收内存是由垃圾回收器完成)

6)避免在终结器中出现异常,比如采用空值检查。

3.使用using语句进行确定性终结:

1)IDisposable接口用一个名为Dispose()的方法释放当前消耗的资源;

2)using语句在对象离开作用域时自动调用Dispose()方法释放资源。

4.垃圾回收和终结

1)f-reachable队列(终结队列):

是准备好进行垃圾回收,同时实现终结的所有对象的一个列表;

2)假如一个对象有终结器,那么“运行时”只有在对象的终结方法被调用之后,才能对这个对象执行垃圾回收;

3)f-reachable队列是一个“引用”列表,一个对象只有在它的终结方法得到调用,而且对象引用从f-reachable队列中删除之后,才会成为“垃圾”。

九、泛型

1.泛型类型概述:

1)利用泛型,可以在声明变量时创建用来处理特定类型的特殊数据结构;

2)参数化类型,使特定泛型类型的每个变量都有相同的内部算法;

3)声明泛型类:

publicclassStack

{

privateT[]_Items;

publicvoidPush(Tdata)

{

….

}

PublicTPop()

{

}

}

2.泛型的优点:

1)提供了一个强类型的编程模型:

确保在参数化的类中,只有成员明确希望的数据类型才可以使用;

2)为泛型类成员使用值类型,不再造成到object的强制转换,它们不再需要装箱操作;

3)性能得到了提高:

不再需要从object的强制转换,从而避免了类型检查;不再需要为值类型执行装箱操作

4)由于避免了装箱,因此减少了堆上的内存的消耗;

3.泛型接口:

1)声明一个泛型接口:

InterfaceIPair

{

TFirst{get;set;}

TSecond{get;set}

}

2)使用泛型接口,就可以避免执行转型,因为参数化的接口能实现更强的编译时绑定;

3)在一个类中多次实现相同的接口:

可以使用不同的类型参数来多次实现同一个接口。

4.多个类型参数:

1)泛型类型可以使用任意数量的类型参数;

2)声明:

InterfaceIPair

{

TFirstFirst{get;set;}

TSecondSecond{get;set}

}

PublicstructPair:

IPair

{

PublicPair(TFirstfirst,TSecondsecond)

{

_First=first;

_Second=second;

}

.

.

.

.

}

5.约束

1)泛型允许为类型参数定义约束。

这些约束强迫类型遵守各种规则;

2)约束声明了泛型要求的类型参数的特征。

3)为了声明一个约束,需要使用where关键字,后跟一对“参数:

要求”;其中,“参数”必须是泛型类型中定义的一个参数,而“要求”用于限制类型从中派生的类或接口,或者限制必须存在一个默认构造器,或者限制使用一个引用/值类型约束。

4)接口约束:

为了规定某个数据类型是必须实现某个接口,需要声明一个接口约束。

不需要执行转型,就可以调用一个显式的接口成员实现。

声明一个接口约束:

publicclassBinaryTree

whereT:

System.IComparable

{

PublicPair>SubItems

{

get{return_SubItems;}

set

{

IComparablefirst;

First=value.First.Item;

…..

}

}

}

5)基类约束:

将构建的类型限制为一个特定的类派生。

6)struct约束:

将类型参数限制为一个值类型;

7)class约束:

将类型参数限制为一个引用类型。

8)多个约束:

对于任何给定的类型参数,都可以指定任意数量的接口作为约束,但基类约束只能指定一个,因为一个类可以实现任意数量的接口,但肯定只能从一个类继承。

publicclassEntityDictionary

:

Dictionary

whereTKey:

IComparable,IFormattable//多个约束接口

whereTValue:

EntityBase//一个基类约束

{

}

9)构造器约束:

可以在指定了其他所有约束之后添加new(),指定类型参数必须有一个默认构造器。

10)继承约束:

约束可以由一个派生类继承,但必须在派生类中显式地指定这些约束。

6.泛型方法

1)即使包容类不是泛型类,或者方法包含的类型参数不在泛型类的类型参数列表中,也依然使用泛型的方法;

2)为了定义泛型方法,需要紧接在方法名之后添加类型参数语法;

7.协变性和逆变性

1)协变性:

将一个较具体的类型赋给较泛化的类型。

2)逆变性:

将较泛化的类型赋给较具体的类型;

3)在C#4.0中使用out类型参数修饰符允许协变性;

4)在C#4.0中使用int类型参数修饰符允许逆变性;

8.泛型的内部机制:

1)基于值类型的泛型的实例化:

“运行时”会为每个新的参数值类型创建新的具体化泛型类型。

2)基于引用类型的泛型的实例化:

使用一个引用类型作为类型参数来首次构造一个泛型类型时,“运行时”会创建一个具体化的泛型类型,并在CIL代码中用object引用替换类型参数。

以后,“运行时”都会重用以前生成好的泛型类型的版本——即使新的引用类型与第一次使用的引用类型不同。

一十、委托、Lambda表达式

1.委托:

将方法作为对象封装起来,允许在“运行时”间接地绑定一个方法调用;

2.所有委托类型都间接从System.Delegate派生,但System.Delegate不能显式的成为一个基类;

3.声明委托数据类型:

PublicdelegateboolComparisonHandler(intfirst,intsecond);

4.实例化委托:

需要和委托类型自身的签名对应的一个方法(相同的参数及参数类型、相同的返回值);

classDelegateSample

{

publicdelegateboolComparisonHanlder(intfirst,intsecond);

publicstaticvoidBubbleSort(int[]items,ComparisonHanldercomparisonMethod)

{

inti,j,temp;

for(i=items.Length-1;i>=0;i--)

{

for(j=1;j<=i;j++)

{

if(comparisonMethod(items[j-1],items[j]))

{

temp=items[j-1];

items[j-1]=items[j];

items[j]=temp;

}

}

}

}

publicstaticboolGreaterThan(intfirst,intsecond)

{

returnfirst>second;

}

staticvoidMain(string[]args)

{

inti;

int[]items=newint[5];

for(i=0;i

{

Console.Write("Enteraninteger:

");

items[i]=int.Parse(Console.ReadLine());

}

BubbleSort(items,GreaterThan);

for(i=0;i

{

Console.WriteLine(items[i]);

}

}

}

5.匿名方法:

1)所谓匿名方法,就是没有实际方法声明的委托实例,它们的定义是直接内嵌在代码中;

2)BubbleSort(items,

delegate(intfirst,intsecond)

{

returnfirst>second;

}

);

6.系统定义的委托:

Func<>

1)在C#3.0中,存在一系列名为“Action”和“Func”的泛型委托;

2)System.Action代表无返回类型的委托;

3)System.Func代表有返回类型的委托;

4)Func的最后一个类型参数总是委托的返回类型,在它之前的类型参数则依次对应于委托参数的类型;

5)可以用一个Func泛型委托来代替一个显式定义的委托。

ComparisonHandler可以用Func来代替

7.Lambda表达式

1)Lambda表达式分为:

语句Lambda和表达式Lambda;

2)Lambda运算符=>,可以理解成“用于”;

3)使用语句Lambda来传递委托:

BubbleSort(items,

(intfirst,intsecond)=>

{

returnfirst>second;

}

);

4)C#要求用一对圆括号来封闭Lambda表达式的参数列表,不管是否指定了这些参数的数据类型;

5)当编译器能判断出数据类型,而且只有一个输入参数的时候,语句Lambda可以带圆括号;

IEnumerableprocesses=Process.GetProcesses().Where(process=>{returnprocess.WorkingSet64>2^30;});

6)语句Lambda含有一个语句块,所以可以包含零个或者更多的语句,而表达式Lambda只有一个表达式,没有语句块;此时将Lambda运算符理解成“满足……条件”;

BubbleSort(items,(first,second)=>first

//理解成first和second满足first小于second的条件

7)Lambda表达式(和匿名方法)并非CLR内部的固有构造,它们的实现是由C#编译器在编译时生成的。

8)persons.Where(person=>person.Name.ToUpper()==”INIGOMONTOYA”);

假定persons是一个Person数组,编译器将Lambda表达式编译成一个Func委托类型,然后将委托实例传给Where()方法。

9)表达式树:

如果一种Lambda表达式代表的是与表达式有关的数据,而不是编译好的代码,这种Lambda表达式就是“表达式树”;

10)表达式树转换成的数据是一个对象图,它由System.Linq.Expressions.Expression表示。

11)Lambda表达式和表达式树的比较:

表达式的语法都会在编译时进行完整的语义分析验证;但是,Lambda表达式在CIL中被编译成一个委托,而表达式树被编译成System.Linq.Expressions.Expression类型的一个数据结构。

一十一、事件

1.多播委托:

一个委托变量可以引用一系列委托,在这一系列委托中,每个委托都顺序指向一个后续的委托,从而形成了一个委托链。

2.使用多播委托来编码Observer模式(又叫publish-subscribe模式):

1)定义订阅者方法:

参数和返回类型必须和来自发布者类的委托匹配

2)定义发布者:

只需一个委托字段,就可以存储所有的订阅者

3)连接发布者和订阅者;

4)调用委托:

当发布者中的属性每次发生变化时,向订阅者通知变化;

//callsubscribers

OnTemperatureChange(value);//将温度的变化发给多个订阅值cooler和heater对象

5)检查空值:

调用一个委托之前,要检查它的值是不是空值;

6)将“-=”运算符应用于委托会返回一个新实例;

7)委托运算符:

+=,获取第一个委托,并将第二个委托添加到委托链中,使一个委托指向下一个委托。

第一个委托的方法调用之后,它会调用第二个委托。

8)+,-,-=,+=运算符,在内部都是使用静态方法System.Delegate.Combine()和System.Delegate.Remove()来实现的。

9)顺序调用

10)例子:

//订阅者1

classCooler

{

privatefloat_Temperature;

publicfloatTemperature

{

get{return_Temperature;}

set{_Temperature=value;}

}

publicCooler(floattemperature)

{

Temperature=temperature;

}

publicvoidOnTemperatureChanged(floatnewTemperature)

{

if(newTemperature>

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 理化生

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1