导图社区 C语言
C#是一种最新的、面向对象的编程语言。它使得程序员可以快速地编写各种基于Microsoft .NET平台的应用程序。本思维导图整理了关于C#语言的主要知识点,包括C#的属性、C#的程序结构、C#的使用三大方面的内容,快来学习吧!
编辑于2020-03-03 16:25:27C#语言
C#的属性
现代的、通用的编程语言。 面向对象。 面向组件。 容易学习。 结构化语言。 它产生高效率的程序。 它可以在多种计算机平台上编译。 .Net 框架的一部分。
C#的功能
布尔条件(Boolean Conditions)
自动垃圾回收(Automatic Garbage Collection)
标准库(Standard Library)
组件版本(Assembly Versioning)
属性(Properties)和事件(Events)
委托(Delegates)和事件管理(Events Management)
易于使用的泛型(Generics)
索引器(Indexers)
条件编译(Conditional Compilation)
简单的多线程(Multithreading)
LINQ 和 Lambda 表达式
集成 Windows
C#产生
进而在2002年推出了Visual Studio(简称VS,是微软提供给开发者的工具集) .NET 1.0版本的开发者平台。而为了吸引更多的开发者涌入平台,微软还在2002年宣布推出一个特性强大并且与.NET平台无缝集成的编程语言,即C# 1.0正式版。 只要是.NET支持的编程语言,开发者就可以通过.NET平台提供的工具服务和框架支持便捷的开发应用程序。 C#就是为宣传.NET而创立的,它直接集成于Visual Studio .NET中, VB也在.NET 1.0发布后对其进行支持, 所以这两门语言与.NET平台耦合度很高, 并且.NET上的技术大多都是以C#编程语言为示例, 所以经常就.NET和C#混为一谈(实质上它们是相辅相成的两个概念)。 而作为一个开发者平台,它不仅仅是包含开发环境、技术框架、社区论坛、服务支持等,它还强调了平台的跨语言、跨平台编程的两个特性。
c#与c++的区别
编译原理不一样
C#中的const与C++中的有所不同,它指编译期常量,而运行期间的常量要用readonly来指定。 C#与Java类似,编译后得到的还不是机器代码,而是运行在虚拟机中的元指令。
变量不可重名
局部变量和全局变量的名称可不可以相同;C++中是可以相同的,且局部变量会覆盖全局变量的值;C#中全局变量和局部变量的名称不可以相同;
变量初始化
C++中当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动对其初始化;而在C#中无论是局部变量还是全局变量系统都会对其初始化;
-不再内存管理
C#有自动垃圾收集机制,防止内存泄露,把C++程序员从繁重的内存管理上解放出来。 没有指针,不能直接操作内存,自动实现内存管理。 C++中的指针在带来强大的灵活性和高效的同时,也带了不少使用上的难题,C++程序中的绝大多数问题都来源于指针的不正确使用, C#出于软件安全性的考虑和语言易用性的考虑没有指针。C#中实现自动垃圾回收,通过new在堆中创建对象,当对该对象的引用计数为0时回收内存。类有构造函数而没有析够函数。
-+参数传递引用
1、抛弃了指针的使用。在c#中没法使用指针了,在对象成员的调用也只有一种方式,就是通过 . 来引用。 C#与Java类似,编译后得到的还不是机器代码,而是运行在虚拟机中的元指令。它对安全性做了更多的考虑,没有指针,不能直接操作内存,自动实现内存管理。 C#没有指针这个概念,只有引用和数值之分。int等内部数据类型和struct定义的类型是数据类型,拷贝时做深度拷贝 ;而string和用class定义的类型是引用类型,拷贝时做浅拷贝——与深度拷贝对应,它通过引用计数来实现对象和内存管理。
传递参数为数组时
21.函数参数类型为数组时: C++:当参数类型为数组时,函数默认当成指针类型来处理,在函数体里改变数组里的值,函数外面也跟着改变; C# :当参数类型为数组时,函数默认当成引用类型来处理,在函数体里改变数组里的值,函数外面也跟着改变;
传递参数为对象时
22.函数参数类型为对象时: C++:当参数类型为对象时,函数在传参时进行对象的复制,在函数体里改变数组里的值,函数外面不会跟着改变; C# :当参数类型为对象时,函数默认当成引用类型来处理,在函数体里改变数组里的值,函数外面也跟着改变;
完全面向对象
2、完全面向对象。在c#中,不再有全局的函数了。所有的代码都必须在类中。连基本的基本类型都有方法,如: 程序的入口Mai()函数是某个对象的public static成员函数。
所有对象从object派生
所有对象都是由Object派生而来,包括内部数据类型int,float,string等,它们只是System.int32等的别名而已。
数组变成类
数组变为了类,因此对于数组里的元素,.NETFramework直接提供了一系列的操作:查找、排序、倒置……
所有的类从system.object

类的函数基本是object函数
.ToString()输出类名;
-格式
C#中没有模板变委托
C#中没有模板,通过将参数设置为Object类型来实现类似的功能,它的downcast类似于C++中的dynamic_cast操作符。 c++它又继承了C语言的简介和优美,尽量用统一的风格实现尽可能多的特性,比如操作符重载、模板等。C#与C++相比较,更加复杂,还显得臃肿和凌乱。
委托
C++的模板在C#里没有了,但在C#中我们找到了能完成模板任务的更锋利的武器:委托。
C#没有头文件
C#没有头文件,变量、函数和类没有定义和申明的区别,都在一起。 只能通过设计抽象类的方式实现代码分离。 C++在这方面虽然做得还不够完美,但比C#强不少。
c#没有宏
C++的宏在C#中被抛弃了很多,而且也不建议使用,因此很少见。
c#没有全局变量
C++的全局变量这一概念没有了,C#和JAVA类似, 要把所有东西都放在类里面,还建议使用命名空间包含起来
使用
类扩充
C#可以在定义类的时候直接给属性赋值,而C++这么做却会编译出错
变量在类中赋值
C#中在类中的成员变量声明处即可初始化,而C++中不行, 两者都可以在构造函数中初始化成员变量。 C#中的静态成员变量可以在静态构造函数中初始化,而静态构造函数是在该类的第一次使用时调用, 而C++中是在编译单元载入时初始化静态成员变量。
-构造函数
C#里有静态构造函数一个概念,这个构造函数只执行一次,因此能够保证一些静态成员只被初始化一遍。
函数变对象
switch
switch容许更多类型
.C++的switch后跟参数必须是int型,而C#却允许string型,这点改进觉得真的是比以前方便多了!
switch禁止更多的失败
C#会禁止所有switch..case语句的失败情形,除非case语句后是空格,否则执行了前一个case语句就算没有break也会停止执行后面的case语句。
foreach增加
C#的语法中多了foreach(typevinlist)statement 而C++中需要用C++标准库的函数实现类似功能。
C#中有属性(Properties)和索引(Index)
C#中有属性(Properties)和索引(Index)。 属性类似于C++中的那些GetValue()和SetValue()成员函数, 只是使用上有些差别。索引类似于C++中的重载操作符[]。
关于路径的写法
C#: 绝对路径:D:\using\\test.txt; 相对路径: using\\test.txt, 表示exe同级目录下的using目录下的test.txt文件; ..\\using\\test.txt表示exe所在目录下上一级目录下的using目录下的test.txt; C++: 绝对路径:D:\\using\\test.txt; 相对路径: .\using\\test.txt,表示VC源码同级目录下的using目录下的test.txt文件; ..\\using\\test.txt 表示VC源码所在目录下上一级目录下的using目录下的test.txt;
类型处理
更强类型转化机制
.更强的类型转换保护机制,比如说把float转成uint,直接转换0.35会变成0.34,是因为二进制无法表示这样的数字,使用System.Conver里的方法,可以安全地把类型安全转换过来。
函数返回全部类型值
函数返回值类型: C#里返回值基本上可以使任何类型,C++则不行,C++的函数返回值类型必须是除了函数和数组类型之外的其他任何类型。
异常处理返回对象
在异常处理上,C++允许抛出任何类型,而C#中规定抛出类型为一个派生于System.Exception的对象。
对象扩充
数组定义
C#定义数组格式是: int[][] arr = new int[4][4]; C++定义数组的格式是:int arr[4][4];
对象定义
3.C#中所有对象都只能通过关键词“new”来创建,C++的“类名_对象名”方式在C#中变为声明一个引用。呵呵,万物皆对象,连常见的数据类型都变为对象了,JAVA味道很浓。 //C++ 两种用法 ClassA objA;objA.FieldA=valueA; //对象法 ClassA * objA=new ClassA();objA->FieldA=valueA;//指针法 //C# ClassA objA=new ClassA();objA.FieldA=valueA
c#与java区别
文件名不同于类名
C# 是大小写敏感的。 所有的语句和表达式必须以分号(;)结尾。 程序的执行从 Main 方法开始。 与 Java 不同的是,文件名可以不同于类的名称。
语言执行特点
面向组件
人机交互的处理方式
查询处理方式
第一种是查询处理方式,比如在DOS系统下、Linux系统等命令行下的程序设计
事件处理机制
由传统的查询法耗费CPU一直在检测,变成了事件处理机制下的主动提醒告知,大幅度减轻CPU资源浪费
C#项目目录

文件后缀
.aspx 是网页文件
.aspx 是网页文件,HTML代码写在这里面。
.cs 源代码
。源代码都写在这里,主要就看这里的代码。
.resx 资源文件
.resx 资源文件,一些资源存放在这里,一般不需要看。
.sln 解决方案文件
sln:在开发环境中使用的解决方案文件。 它将一个或多个项目的所有元素组织到单个的解决方案中。 此文件存储在父项目目录中.解决方案文件,他是一个或多个.proj(项目)的集合
项目文件夹
Bin 编译结果
Bin 目录用来存放编译的结果,bin是二进制binrary的英文缩写,因为最初C编译的程序文件都是二进制文件, 它有Debug和Release两个版本, 分别对应的文件夹为bin/Debug和bin/Release,这个文件夹是默认的输出路径, 我们可以通过:项目属性—>配置属性—>输出路径来修改。
debug调试版本
release发行版本
obj中间临时文件
obj是object的缩写,用于存放编译过程中生成的中间临时文件。 其中都有debug和release两个子目录, 分别对应调试版本和发行版本, 在.NET中,编译是分模块进行的,编译整个完成后会合并为一个.DLL或.EXE保存到bin目录下。 因为每次编译时默认都是采用增量编译,即只重新编译改变了的模块,obj保存每个模块的编译结果,用来加快编译速度。是否采用增量编译,可以通过:项目属性—>配置属性—>高级—>增量编译来设置。
debug调试版本
release发行版本
Properties文件夹
Properties文件夹 定义你程序集的属性 项目属性文件夹 一般只有一个 AssemblyInfo.cs 类文件,用于保存程序集的信息,如名称,版本等,这些信息一般与项目属性面板中的数据对应,不需要手动编写。 
form.Designer.cs 窗体初始化
.Designer.cs 设计文件,自动生成,不需要看。
form.cs窗体控件功能
form.res窗体资源
program.cs主程序入口
项目.csproj.user配置文件
.csproj.user 是一个配置文件,自动生成的,会记录项目生成路径、项目启动程序等信息。也不需要看。
项目.csproj 项目文件
.csproj C#项目文件,用VS打开这个文件就可以直接打开这个项目,自动生成,不需要看。
项目.sln解决方案项的引用
*.sln:(Visual Studio.Solution) 通过为环境提供对项目、项目项和解决方案项在磁盘上位置的引用,可将它们组织到解决方案中。
框架

using System命名空间
程序的第一行 using System; - using 关键字用于在程序中包含 System 命名空间。 一个程序一般有多个 using 语句。 命名空间的设计目的是提供一种让一组名称与其他名称分隔开的方式。 在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。
命名空间(namespace)
在C#中用命名空间将很多类的属性及其方法进行封装供调用,类似C语言中将变量和函数封装成一个个.h文件, 调用的时候只需要#include "filepath + filename"就可以使用, 比如刚开始时用关键字using声明了一些所需要的系统命名空间(line1-10);然后采用关键字namespace来自定义一个用户工程所需的命名空间HelloWorld,在我们定义的这个命名空间里就可以定义一些类和方法来进行下一步的实现;
Namespace declaration
命名空间声明
特性Attribute
特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。息的声明性标签。 您可以通过使用特性向程序添加声明性信息。 一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的 预定义特性(Attribute) .Net 框架提供了三种预定义特性:
AttributeUsage
如何使用一个自定义特性类。 它规定了特性可应用到的项目的类型。 
Conditional条件编译
这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符。 它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace 
Obsolete丢弃目标
这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素   
class类

事件
定义事件
class的声明
class成员
Class 属性 成员变量

get和set关键字
属性的定义与字段类似 来定义,可以控制属性的访问级别
getset自动属性
 按通常的方式定义属性的名称、类型和可访问性, 但是没有提供get和set的实现代码, 实现代码由编译器提供 (私有字段的名字也由编译器提供,将set变成 private set即变成只读)
Class 方法成员函数
定义方法
 
Access Specifier:
访问修饰符,这个决定了变量或方法对于另一个类的可见性。
Return type:
返回类型,一个方法可以返回一个值。返回类型是方法返回的值的数据类型。如果方法不返回任何值,则返回类型为返回类型,一个方法可以返回一个值。返回类型是方法返回的值的数据类型。如果方法不返回任何值,则返回类型为 void。。
Method name:
方法名称,是一个唯一的标识符,且是大小写敏感的。它不能与类中声明的其他标识符相同。
Parameter list:
参数列表,使用圆括号括起来,该参数是用来传递和接收方法的数据。参数列表是指方法的参数类型、顺序和数量。参数是可选的,也就是说,一个方法可能不包含参数。
值参数
这种方式复制参数的实际值给函数的形式参数,实参和形参使用的是两个不同内存中的值。在这种情况下,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。 
ref引用参数
这种方式复制参数的内存位置的引用给形式参数。这意味着,当形参的值发生改变时,同时也改变实参的值。 
out输出参数
这种方式可以返回多个值。return 语句可用于只从函数中返回一个值。但是,可以使用 输出参数 来从函数中返回两个值。 输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。 
Method body:
方法主体,包含了完成任务所需的指令集。
调用方法
class嵌套类

Main 方法

对象实例化
c#程序结构
关键字
方法函数
static静态方法
方法的定义:看见static就是静态方法 [访问修饰符] [static] 返回值类型 方法名() { 方法体; }
out返回参数
1)、out参数。 如果你在一个方法中,返回多个相同类型的值的时候,可以考虑返回一个数组。 但是,如果返回多个不同类型的值的时候,返回数组就不行了,那么这个时候, 我们可以考虑使用out参数。 out参数就侧重于在一个方法中可以返回多个不同类型的值。
out只出不进传至可以不初始化
ref引用参数
调用引用函数 能够将一个变量带入一个方法中进行改变,改变完成后,再讲改变后的值带出方法。 ref参数要求在方法外必须为其赋值,而方法内可以不赋值。 <FunctionName>(ref <paraName>) static <returnType> <FunctionName>(ref <paramType><paraName>,...) { ... renturn <returnValue>; }
ref有进有出传值必须要初始化
params可变参数
将实参列表中跟可变参数数组类型一致的元素都当做数组的元素去处理。 params可变参数必须是形参列表中的最后一个元素。
event
event 关键字用于在发行者类中声明事件。
class类
封装访问权限
无或 Interna只能在当前项目中访问 public可以在任何地方访问类 abstract或 internal abstract塽类只能在当前项目访问,不能实例化,只能被继承 public abstraci类可以在任何地方访问,不能实例化,只能继承 sealed或 Internal sealed类只能在当前项目访问,不能被继承,只能实例化 public sealed类可以在任何地方访问,不能被继承只能实例化 封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。 抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化,封装则使开发者实现所需级别的抽象。 C# 封装根据具体的需要,设置使用者的访问权限,并通过 访问修饰符 来实现。
public:所有对象都可以访问;
private:对象本身在对象内部可以访问;
internal:同一个程序集的对象可以访问;
继承:protected:只有该类对象及其子类对象可以访问
多态:protected internal访问限于当前程序集或派生自包含类的类型
派生类的可访问性不能高于基类
继承:sealed不能继承
密封的 sealed不能继承 如果基类是抽象的,在派生类必须全部实现 
实例:abstract抽象不能实例
类是抽象的,不能实例化,只能继承 abstract,
abstract 抽象类
C# 允许您使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。 当一个派生类继承自该抽象类时,实现即完成。 抽象类包含抽象方法, 抽象方法可被派生类实现。 派生类具有更专业的功能。
partial分割而编译为一个类
1.最关键的一点,这里类也是用partial关键字修饰的,可以看到,Partial是局部类型的意思, 允许我们将一个类、结构或接口分成几个部分, 分别实现在几个不同的.cs文件中,用partial定义的类可以在多个地方被定义,最后C#编译器编译时会将这些类当作一个类来处理;
interface接口参数
不能有关键字 statIc/rtua|/ abstract/ sealed来定义接口
接口没访问修饰符
继承:隐藏基类成员加关键字new
成员属性方法
readonly表示字段只能在执行构造函数的过程中赋值,
unsafe不安全上下文
unsafe 关键字表示不安全上下文,该上下文是任何涉及指针的操作所必需的
volatile不优化
volatile 关键字指示一个字段可以由多个同时执行的线程修改。声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。这样可以确保该字段在任何时间呈现的都是最新的值。
statIc静态变量
使用 static 修饰符声明属于类型本身而不是属于特定对象的静态成员。 static 修饰符可用于类、字段、方法、属性、运算符、事件和构造函数, 但不能用于索引器、析构函数或类以外的类型。
const也是静态的
const 关键字用于修改字段或局部变量的声明。它指定字段或局部变量的值是常数,不能被修改。
extern方法定义在其他地方
重写:sealed不能由派生类重写
来指定在派生类中不能对这个方法做进一步修改即这个方法不能由派生类重写
重写:new隐藏父类同名方法
关键字在子类方法上使用时会隐藏父类的同名方法
重写:virtua方法可以重写
virtual 关键字用于修饰方法、属性、索引器或事件声明,并使它们可以在派生类中被重写。例如,此方法可被任何继承它的类重写。
重写:override方法重写
如果使用了方法重写了一个基类方法(如果方法被重写,就必须要使用关键字 override,
重写:abstract坊方法必须在非抽象的派生类中重写
(只用于抽象类中) abstract 修饰符可以和类、方法、属性、索引器及事件一起使用。在类声明中使用 abstract 修饰符以指示某个类只能是其他类的基类。标记为抽象或包含在抽象类中的成员必须通过从抽象类派生的类来实现
标点符号
运算符
 
注释
/* */和一行// ///可以通过配置,将这些注释提取出来组成文档文件
;结束一条C#语句
逗号 将多个命合放在同一行
圆括号
函数
改变运算优先级
《》泛型
{ }表示一段代码块
转义字符
\' 单引号 0x0027 \'' 双引号 0x0022 \\ 反斜杠 0x005c \0 空 0x0000 \a 警告 0x0007 \b 退格 0x0008 \f 换页 0x000C \n 换行 0x0000A \r 回车 0x000D \t 水平制表符 0x0009 \v 垂直制表符 0x000B @转义字符,避免大量使用\
数据结构
值类型
数字、字符、浮点数 值类型变量可以直接分配给一个值。它们是从类 System.ValueType 中派生的。 值类型直接包含数据。比如 int、char、float,它们分别存储数字、字符、浮点数。
基本数据类型
整型
sbyte System.SByte -128~127 byte System.Byte 0~255 short System.Int16 -32768~32767 ushort System.UInt16 0~65535 int System.Int32 -21亿~21亿 uint System.UInt32 42亿 long System.Int64 19位数字 ulong System.UInt64 20位数字
int有符号的32位
long有符号的64位
byte无符号的8位
ushort无符号的16位
uint无符号的32位
uong无符号的64位
浮点型
位是最小数据单位,只能表示0-1 字节,8个二进制位构成1个字节,是存储空间的基本计量单位, 字:由若干字节构成,不同计算机有不同的字长, 8位计算机一个字等于一个字节,16位一个字等于两个字节,字是计算机数据处理和运算的单位 1kb等于1024个字节 1M等于1024KB 1G等于1024M 1T等于1024G float System.Single 4字节 double System.Double 8个字节 decimal System.Decimal 16个字节
foat有符号的32位
double有符号的64位
decimal
有符号的专门用来做财务计算的形如有符号的专门用来做财务计算的形如: decimal m=8.55M
布尔类型
char System.Char Unicode 0~65535 2个字节 bool System.Boolean 1个字节 string System.String
bool my Bool=false,
字符型char
枚举

结构体

可空类型(Nullable)
C# 提供了一个特殊的数据类型,nullable 类型(可空类型) 可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值。 例如, Nullable,读作"可空的 Int32",可以被赋值为 -2,147,483,648 到 2,147,483,647 之间的任意值, 也可以被赋值为 null 值。 Nullable 变量可以被赋值为 true 或 false 或 null。 在处理数据库和其他包含可能未赋值的元素的数据类型时,将 null 赋值给数值类型或布尔型的功能特别有用。例如,数据库中的布尔型字段可以存储值 true 或 false,或者,该字段也可以未定义。
单问号 ?
? : 单问号用于对 int,double,bool 等无法直接赋值为 null 的数据类型 进行 null 的赋值, 意思是这个数据类型是 NullAble 类型的。 
合并运算符??
Null 合并运算符用于定义可空类型和引用类型的默认值。Null 合并运算符为类型转换定义了一个预设值,以防可空类型的值为 Null。Null 合并运算符把操作数类型隐式转换为另一个可空(或不可空)的值类型的操作数的类型。 如果第一个操作数的值为 null,则运算符返回第二个操作数的值,否则返回第一个操作数的值 
引用类型
内置的 引用类型有:object、dynamic 和 string。
对象(Object)类型
对象之间关系
包含关系
包含关系,这个成员字段可以是公共字段, 此时是继承关系一样,容器对象的用户就可以访问它的方法和属性, 与继承不同的是,不能访问类的内部代码,类变成其私有变量
集合关系
集合关系,增加了功能的数组
动态(Dynamic)类型
字符串(String)类型
char
string
char mychar ="a"
string mystring ="a"
数组

参数数组
,C#允许函数指定一个特殊参数,该参数必须是函数的最后一个,称为 static <returnType> <FunctionName>(<p1Type><p1Name>,...Params<type>[]<name>) { ... renturn <returnValue>; }普通函数定义 举例 class Prigram { static int SunVals(params int [] vals) { int sum=0; foreach(int val in vals) { sum+=val; } return sum; } static void Main(string [] args) { int sum=SumVals(1,5,2,9,8); console.writeline("summed values={0}",sum); } }
接口
把公共实例 非静态方法和属性组合起来,以封装特定功能的一个集合接口不能实例化 IDisposable接口的对象必须实现其 Dispose0方法, 当不再需要某个对象时就调用这个方法,释放重要资源 C#简化了这种方法, usIng关键字可以在代码块中初始化使 用重要资源的对象,这个代码块的结尾会自动调用Dispose0方法 
接口的功能
接口最简单的作用就是:不同的类完成相同的行为 而且这个行为要在子类中实现 比如,在系统分析时我们只使用了两个类,一个是dog,一个是cat,他们都可以实现一个shot的行为, 如果两者均有的情况下,原来是可以将shot行为提升到dog与cat的父类中,也就是动物类中实现,由于系统中并没有对两个类进行提升,也就是说,这当前系统中不存在dog与cat的父类, 那么,我们可以实现一个接口去解决这两个不同的类的同一行为,就是定义Ishot接口,让两个不同的类进行继承!这样两个类可以实现同一行为了!
接口属性
0.接口定义
 接口只是定义了 但是没有实现
1.接口继承
以下实例定义了两个接口 IMyInterface 和 IParentInterface。 如果一个接口继承其他接口,那么实现类或结构就需要实现所有接口的成员。 实例 IMyInterface 继承了 IParentInterface 接口, 因此接口实现类必须实现 MethodToImplement() 和 ParentInterfaceMethod() 方法: 
实现接口成员多态

接口实现多态
接口的多态性,尽管不能实例化接口,可以建立接口类型的变量, 然后就可以在支持该接口的对象上,使用这个变量来访问接口提供的方法和属性 Cow my Cow=new Cow0 Chicken my Chicken =new Chicken IConsume consumeInterface consumeInterface my Cow consumeInterface. EatFoodO consumeIntertace mychIcken consumeInterface. EatFoodo 例如不适用基类提供的方法,而是把该方法放在接口上,实例化也支持该接口, 唯一的区别是方法的实现代码不一样 (接口不包含方法的实现), 通过接口可以实现访问多个对象的方法而不依赖于一个公共的基类
接口成员
接口成员没有实现
如果一个接口继承其他接口,那么实现类或结构就需要实现所有接口的成员
接口没有字段成员但是接口可以定义自动属性
类
构造函数
构造函数和析构函数,和C++一样 静态类不能实例化对象,只能包含静态成员, 统—建模语言 Unified Modeling Language(UML)
无参构造函数
会调用两次构造函数
先调用父美关中无参的构适语数,然后是己的无参构造话款O无参构造数
形式:e
public class Derivedclass(
public Derivedclass0base{ do something}/base(可以省略不与
构造函数和析构函数,和C++一样
静态构造函数
创建包含静态构造函数的类实例 静态构造函数H访问包含静态构造函数的类的静态成员时 (无论创建多少个实例,静态构造函数只调用一次。)
默认构造函数
:base(i) :this(5,6)调用自己的两个参数的构造函数,可以伪装成
析构函数
素引器
类的定义
定义类时使用 public partial class MyClass { }
把类的定义放在不同的文件中partial
部分方法定义 public partial class MyClass { partial void DoSomethingElse(); public void DoSomething() { console.writeline("do something started"); DoSomethingElse(); console.writeline("do something ended"); } } public partial class MyClass { partial void DoSomethingElse() { console.writeline("DoSomethingElse called"); } } 如果删除部分方法的实现部分,编译时编译器会以为调用了一个空的部分方法会直接删除部分方法的调用
属性的定义
与字段类似,get和set关键字来定义,可以控制属性的访问级别 private int myInt; public int myIntProp { get { return myInt; } public set { if(value>0&&value<10) myInt=value; else throw(new ArgumentOutOfRangeException("MyIntProp",value,"MyIntProp must be assigned a value between 0 and 10")); } }
右击refactor(重构)-Encapsulate Field可以快熟添加属性
自动属性:
public int MyIntProp { get;set; }按通常的方式定义属性的名称、类型和可访问性, 但是没有提供get和set的实现代码, 实现代码由编译器提供(私有字段的名字也由编译器提供, 将set变成private set即变成只读)
定义类成员
隐藏
class Animal { public void EatFood() { Console.WriteLine("Animal is adjusted"); } } class Chicken : Animal { public void EatFood()//new public void EatFood() { Console.WriteLine("Chicken is adjusted"); } } class Cow : Animal { public void EatFood()//new public void EatFood() { Console.WriteLine("Cow is adjusted"); } } 隐藏基类方法,会产生2个警告(如果要确定隐藏需要加上new),无论基类怎样限制,只要派生类没有调用override都是影藏
要对派生类的用户隐藏继承的公共成员,但仍能访问其功能 要给继承的虚拟成员添加实现代码,而不是简单地用重写的新实现代码替换它 可以使用base关键字:base.DoSomething()
this最常用的功能是将当前对象实例的引用传递给一个方法,该方法有一个参数是指向基类的 this关键字的另一个常用方法是限定局部类型成员
嵌套类型成员
public class MyClass { public class MyNestedClass { public int NestedClassField; } }实例化时: MyClass.MyNestedClass myObj=new MyClass.MyNestedClass();
public class ClassA { private int state = -1; public int State { get { return state; } } public class ClassB { public void SetPrivateState(ClassA target, int newState) { target.state = newState; } } } class Program { static void Main(string[] args) { ClassA myObject = new ClassA(); Console.WriteLine("myObject.State = {0}", myObject.State); ClassA.ClassB myOtherObject = new ClassA.ClassB(); myOtherObject.SetPrivateState(myObject, 999); Console.WriteLine("myObject.State = {0}", myObject.State); Console.ReadKey(); } }嵌套类修改只读变量
属性与字段
字段和属性都属于类的成员
字段
字段(field) 用来存储数值或对象的真正实体 简单说是私有变量(不一定是私有...)
属性
属性(property) 对字段的封装(也不一定- -) 有get段落和set段落 通常是公开的 通常get段和set段执行过程都是轻量的(这是一个语义约定 也是C#不同于java的地方 重量级 耗时的获取也要用函数来表达而不是属性 java就没这种语义了 一律get- -)
区别
访问性不同
1、C#中属性:C#中属性是限制只能给变量赋于某个范围的值,是有限制的访问私有变量。 2、C#中字段:C#中字段是自由的、毫无限制的访问公有变量。
包含不同
1、C#中属性:C#中属性中包含两个块:set和get, set块负责属性的写入工作, get块负责属性的读取工作。 2、C#中字段:C#中字段中不包含set和get两个块,可以直接进行字段写入和读取。
安全性不同
1、C#中属性:C#中属性因为是私有的、写入和读取需要调用set和get块,所以当不在本类中使用时可以保证使用属性的安全性。 2、C#中字段:C#中字段因为是公有的、写入和读取不需要调用set和get块,所以当不在本类中使用时无法保证使用字段的安全性。
delegate委托调用
函数的引用 委托,和重载差不多,只不过重载特征表不同, 而委托特征1标相同而函数名不同,可以根据需求NEW出不同的函数, 
功能
委托的利用在于可以将其当成参数传递给函数,让函数因需求执行不同的操作,类似于多态
声明
定义了委托后,就可以声明该委托类型的变量,接着把这个变量初始化为与委托具有相同返回类型和参数列表的函数引用
组成
委托是一种存储函数引用的类型 委托声明类似于函数,但是不带函数体, 且要使用关键字 delegate,委托指定了一个返回类型和一个参数列表
调用
之后就可以使用委托变量调用这个函数 例子,可以把委托变量作为参数传递给一个函数,这样,该函数就可以使用委托调用它引用的任何函数 
指针类型
指针类型(Pointer types) 指针类型变量存储另一种类型的内存地址。C# 中的指针与 C 或 C++ 中的指针有相同的功能。 声明指针类型的语法: type* identifier; 例如: char* cptr; int* iptr;
算法
类型转换
自动类型转换(隐士类型转换)
小的转大的(int--->double)
子类转化为父类
显示类型转换(强制类型转换)
大的转小的(double--->int)
语法:(待转换的类型)要转换的值
强制转换
n_int=Convert.ToInt32();强制转换成int ToBase64CharArray 将 8 位无符号整数数组的子集转换为用 Base 64 数字编码的 Unicode 字符数组的等价子集。 ToBase64String 将 8 位无符号整数数组的值转换为它的等效 String 表示形式(使用 base 64 数字编码)。 ToBoolean 将指定的值转换为等效的布尔值。 ToByte 将指定的值转换为 8 位无符号整数。 ToChar 将指定的值转换为 Unicode 字符。 ToDateTime 将指定的值转换为 DateTime。 ToDecimal 将指定值转换为 Decimal 数字。 ToDouble 将指定的值转换为双精度浮点数字。 ToInt16 将指定的值转换为 16 位有符号整数。 ToInt32 将指定的值转换为 32 位有符号整数。 ToInt64 将指定的值转换为 64 位有符号整数。 ToSByte 将指定的值转换为 8 位有符号整数。 ToSingle 将指定的值转换为单精度浮点数字。 ToString 将指定值转换为其等效的 String 表示形式。 ToUInt16 将指定的值转换为 16 位无符号整数。 ToUInt32 将指定的值转换为 32 位无符号整数。 ToUInt64 将指定的值转换为 64 位无符号整数。 n_double=Convert.ToDouble();强制转换成double Convert.ReadKey( ); (enumerationType)Enum.Parse(typeof(enumerationType),enumerationValueString);字符串转换成枚举
字符串技巧
.ToCharArray()这个函数可以将String编程char[]数组
.Length字符串长度
.ToLower字符串全部小写
.ToUpper字符串全部大写
.Trim字符串删除空格
.PadLeft()和.PadRight()字符串左边或者右边添加空格
.IndexOf(',');定位到,的下标
.Split(' ');切片
集合(Collection)类
栈(stack)
队列(queue)
列表(list)
哈希表(hash table)
集合比较转换
集合:可以使用集合来维护对象组
集合大多是通过System.Collections名称空间中的接口而获得的,集合的语法已经标准化了
System.Collections包括几个接口: IEnumerable可以迭代集合中的项 ICollection继承上一个接口,可以获取集合中项的个数,并能把项复制到一个简单的数组类型中 IList继承上两个接口,提供了集合的项列表,允许访问这些项,并提供一些基本功能 IDictionary继承与前两个接口,可以通过键值访问项列表
System.Array类实现了IList,ICollection,IEnumerable接口 System.Collections名称空间中的类System.Collections,ArrayList也实现了这些接口并且更为复杂
定义集合通过Collections
直接定义比较复杂可以派生System.Collection.CollectionBase,同时该类提供了两个受保护的属性,List和InnerList using System.Collections; public class Cards:CillectionBase { }
索引符是一种特殊类型的属性 public Animal this[int animalIndex] { get { return (Animal)List[animalIndex]; } set { List[animalIndex]=value; } }
定义集合,通过IDictionary
基类为DictionaryBase,提供了一个Dictionary属性 public void Add(string newID,Animal newAnimal) { Directionary.Add(newID,newAnimal); } public void Remove(string animalID) { Directionary.Remove(animalID); } 索引符是一种特殊类型的属性 public Animal this[string animalID] { get { return (Animal)Directionary[animalID]; } set { Directionary[animalID]=value; } }
其可以使用foreach迭代器 foreach(DictionaryEntry myEntry in animalCollenction) { console.writeline("{0}{1}",myEntry.Value.ToString(),((Animal)myEntry.Value).Name); }
迭代器在foreach循环中,迭代一个collectionObject集合
foreach
调用collectionObject.GetEnumerator(),返回一个IEnumerator引用
调用所返回的IEnumerator接口的MoveNext()方法
如果MoveNext()返回true.就使用IEnumerator接口的Current属性获取对象的引用,用于foreach循环
重复前两步,知道返回false
迭代器
两种可能返回类型是前面提到的接口类型IEnumerable和IEnumerator 如果要迭代一个类,可使用方法GetEnumerator()其返回类型IEnumerator; 如果要迭代一个类成员,例如一个方法,则使用Enumerable yield return<value>;
public static IEnumerable SimpleList() { yield return "string 1"; yield return "string 2"; } static voud Main() { foreach(string item in SimpleList()) }
深度复制
实现ICloneable接口,该接口有一个Clone()方法,
public class Content { public int val; } public class Cloner :IConeable { public Content MyContent=new Content(); public Cloner(int newVal) { MyContent .val=newVal; } public object Clone() { Cloner clonedCloner =new Cloner (MyContent.Val); return cloneCloner; } }
比较:在处理对象时,常要比较他们
类型比较
if(myObj.GetType()==typeof(MyComplexClass))
封箱和拆箱
封箱就是把值类型转换为System.Object类型, 拆箱就是把System.Object类型转换为值类型
struct MyStruct { public int Val; } MyStruct valType=new MyStruct(); valType.Val=5; object.refType=valType; 这样称为封箱,这种封装只是封装的原值的副本
class MyStruct { public int Val; } MyStruct valType=new MyStruct(); valType.Val=5; object.refType=valType; 这样称为封箱,这种封装封装原值
interface IMyInterface {} struct MyStruct:IMyIterface { public int Val; } MyStruct valType=new MyStruct(); valType.Val=5; IMyInterface refType=valType;
MyStruct ValType2=(MyStruct)refType;解箱
封箱允许在项的类型是object的集合中使用值类型, 在一个内部机制允许在值类型上调用object方法
is运算符不是用来说明对象是某种类型,而是用来检查对象是不是给定类型,或者是否可以转换成给定类型
<operand>is <type> 如果type是一个类类型,operand也该是该类类型 如果type是一个接口类型,operand也该是该类型 如果type是一个值类型,那么operand也是该类型
值比较
运算符重载
public static AddClass1 operator +(AddClass1 op1,AddClass1 op2) { AddClass1 returnValue=new AddClass1(); returnVal.val=op1.val+op2.val; return returnVal; }
+,-,!,~,++,--,true,false +,-,*,%,/,&,|,^,<<,>> ==,!=,<,>,<=,>= 可以重载的运算符
使用IComparable和IConparer
IComparable比较对象中类的实现,可以比较一个对象和另一个对象 IComparer在一个单独的类中实现,可以比较任意两对象
IComparable中有一个CompareTo()方法,这个方法接受一个对象 if(person1.CompareTo(person2)==0)
IComparer提供了一个Compare(),这个方法接受两个对象,该方法接受两个对象 personCompare(person1,person2);
string firststring="first string"; string secondstring = "second string"; Console.writeline("{0}{1}{2}",firststring,secondstring,Comparer.Default.Compare(firststring ,secondstring)); Comparer.Default静态成员获取Comparer类的实例,然后使用Compare比较
注意事项: 在使用conparer时,必须是可以比较的类型,如果试图比较string和int类型就会发生异常, 检查传送给Comparer.Compare()的对象,看他们是否支持IComparable,支持实现该代码 允许使用NULL值,它表示小于其他任意对象
这两种方法需要比较某种类型,不然会抛出异常
对集合的排序
System.Collections名称空间中的类都没有提供排序方法,Arraylist类提供了sort()排序算法,对于自己的类使用IComparer进行比较排序
public class Person : IComparable { public string Name; public int Age; public Person(string name, int age) { Name = name; Age = age; } public int CompareTo(object obj) { if (obj is Person) { Person otherPerson = obj as Person; return this.Age - otherPerson.Age; } else { throw new ArgumentException( "Object to compare to is not a Person object."); } } }
public class PersonComparerName : IComparer { public static IComparer Default = new PersonComparerName(); public int Compare(object x, object y) { if (x is Person && y is Person) { return Comparer.Default.Compare( ((Person)x).Name, ((Person)y).Name); } else { throw new ArgumentException( "One or both objects to compare are not Person objects."); } } }
using System.Collections; class Program { static void Main(string[] args) { ArrayList list = new ArrayList(); list.Add(new Person("Jim", 30)); list.Add(new Person("Bob", 25)); list.Add(new Person("Bert", 27)); list.Add(new Person("Ernie", 22)); list.Sort(); list.Sort(PersonComparerName.Default); } }
转换:定义类型转换
如果既没有继承,又没有公有接口的类如果转换
ConClass1 op1=new ConClass1(); ConClass2 op2=op1;//隐式转换
ConClass1 op1=new ConClass1(); ConClass2 op2=(ConClass2 )op1;//显示转换
public class ConvClass1 { public int val; public static implicit operator ConvClass2(ConvClass1 op1)//允许隐式转换 { ConvClass2 returnVal=new ConvClass2(); returnVal.val=op1.val; return returnVal; } } public class ConvClass2 { public double val; public static explicit operator ConvClass1(ConvClass2 op1)//只能显式转换 { ConvClass1 returnVal=new ConvClass1(); checked(returnVal.val=(int)op1.val); return returnVal; } }
as运算符 <operand>as<type> operand类型是type类型 operand可隐式转换为type类型 operand可以封箱到type类型 如果operand不能转化为type则表达式结果为null
class ClassA:IMyInterface {} class ClassB:ClassA {} ClassA ob1=new ClassA (); ClassD ob2=ob1;//抛出异常 ClassD ob2=ob1 as ClassB;//不会抛出异常但是ob2的值为null;
逻辑结构
表达式(Expressions)
跳转
goto语句 goto <labelName> <labelName>:
选择
if else

goto

switch

三元
三元运算符: <test>?<resultIfTrue>:<resultIfFalse>
循环
do while

while
while(<test>) { <code to looped> }
for

for each

跳出
循环的中断: break立刻终止循环 continue立刻终止当前循环 goto可以跳出循环 return跳出循环,即包含循环的函数
处理异常
try { 有可能出现错误的代码; } catch { 如果try中的代码出现异常,则进去catch中 } finally { 最后执行的代码 } 在 try 块中获取并使用资源,在 catch 块中处理异常情况,并在 finally 块中释放资源。 throw:
基本函数方法
static <returnType> <FunctionName>(<paramType><paraName>,...) { ... renturn <returnValue>; }普通函数定义
控制台
Console.WriteLine("字符串"); Console.WriteLine("{0}{1}{2}",n_a,n_b,n_c);
str=Console.ReadLine();
调试
打断点
可以查看输出窗口,输出/调试, 语句Debug.WriteLine()将调试信息显示到调试窗口 Trace.WriteLine(),用法相同,可以用于发布 Debug.WriteLine("Add 1 to i","MyFunc");结果为: MyFunc:Add 1 to i; using System.Diagnostics; Debug.WriteLineIf()增加一个必选项,当是真的时候进行输出
跟踪点,有点类似于Debug.WriteLine()
Trace.Assert(myVar<0,"variable out of bounds","please contact vendor with the error code KCW001.")
异常
try {...} catch(<exceptionType>e) {...} finally {...} try抛出异常,catch抛出异常时执行的代码,finally包含最终执行的代码 即使try中有return, finally还是会执行,在finally中还是会改变return中的值
抛出异常包括内容 throw(new ArgumentOutOfRangeException("MyIntProp",value,"MyIntProp must be assigned a value between 0 and 10")); catch中捕获异常显示 catch (Exception e) { Console.WriteLine("Exception {0} thrown.", e.GetType().FullName); Console.WriteLine("Message:\n\"{0}\"", e.Message); }
C#的使用
变量使用
全局变量:Program.全局变量,在声明变量前要进行初始化
先声明后使用
数组的使用
数组的声明
保存多个值;几乎任意类型都可以声明数组; int[] nums = new int[3]; int[] nums = {5,3,8}; in[]nums=new int[3]{1,2,3}; int[]nums=new int[]{2,3,5}; 数组的声明:以上四种
个数与声明数需要一致
int[] nums = new int[3]{5,3,8}//个数和声明数必须一致 int[] nums = new int[5]{5,3,8}//错误 int[] nums = new int[]{5,3,8}//正确,可以省略数组个字 使用索引器访问指定编号位置的元素,访问数组元素:nums[0]、nums[1]。索引从0开始。取到的元素的类型就是数组元素的类型。还可以对数组元素进行赋值
方法的使用
参数的传递
如果被调用者想要得到调用者的值: 1)、传递参数。 2)、使用静态字段来模拟全局变量。 如果调用者想要得到被调用者的值: 1)、返回值
函数的重载调用
就同名不同参数 构成重载的条件:参数类型不同或者参数个数不同(不严谨的),与返回值无关。  概念:方法的重载指的就是方法的名称相同给,但是参数不同。 参数不同,分为两种情况 1)、如果参数的个数相同,那么参数的类型就不能相同。 2)、如果参数的类型相同,那么参数的个数就不能相同。 ***方法的重载跟返回值没有关系。
特征标不一样就行 引用和非引用也属于重载
泛型(Generic)
功能
范型的意义在于免去了类型之间互相转换的系统开销,和同类方法的重载, 比如,Add方法你要重载两个方法(int和double)或者更多方法,用范型只用写一个Add方法就可以完成int,double,float......等等的相加, 再如,集合的操作,没有往往是弱类型(object),而用范型可以直接是强类型,无需转换之间的开销,节省了资源,
泛型委托、泛型接口,泛型的方法重载
泛型约束
在同一类中名称的使用
命名规则:方法名开头大写,参数名开头小写,参数名、变量名要有意义 方法的调用,对于静态方法,调用有两种方式 如果在同一个类中,直接写名字调用就行了. 或者类名.方法名(); return可以立即退出方法.
面向对象编程
类的使用
类是抽象的,不能实例化,只能继承abstract,密封的sealed不能继承 如果基类是抽象的,在派生类必须全部实现
类还可以指定接口,同时必须实现该接口的所有成员,如果不想使用给定的接口成员,就可以提供一个空的实现方法, 先继承类(只能有一个)再继承接口,接口可以有多个
静态类不能实例化对象,只能包含静态成员,
类的命名
标识符必须以字母、下划线或 @ 开头,后面可以跟一系列的字母、数字( 0 - 9 )、下划线( _ )、@。 标识符中的第一个字符不能是数字。 标识符必须不包含任何嵌入的空格或符号,比如 ? - +! # % ^ & * ( ) [ ] { } . ; : " ' / \。 标识符不能是 C# 关键字。除非它们有一个 @ 前缀。 例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字。 标识符必须区分大小写。大写字母和小写字母被认为是不同的字母。 不能与C#的类库名称相同。
1.继承
protected privated publIC class InterfaceImplementer : IMyInterface
基类
纯虚基类
纯虚基类,重写方法,也可以不重
抽象基类
抽象基类不能实例化, 抽象基类必须被继承, 成员没有实现代码,在派生类中实现他们抽象基类在UML中为斜体
派生类
类还可以指定接口,同时必须实现该接口的所有成员,如果不想使用给定的接口成员,就可以提供一个空的实现方法, 先继承类(只能有一个)再继承接口,接口可以有多个
接口
2.多态
通过实例化不同的派生类调用派生类的重写方法 将派生类赋给基类调用基类重写方法, 实际上调用了基类的重写方法  自动的按照接口 自动入位   
静态多态性
函数重载
运算符重载
动态多态性
函数的响应是在运行时发生的。 在 C# 中,每个类型都是多态的,因为包括用户定义类型在内的所有类型都继承自 Object。 多态就是同一个接口,使用不同的实例而执行不同操作
多态测试,使用virtual和override多态测试成功
事件
对象可以激活和使用事件
事件必须订阅,可以由多个订阅
using System.Timers; class Program { static int counter = 0; static string displayString = "This string will appear one letter at a time. "; static void Main(string[] args) { Timer myTimer = new Timer(100);//创建定时器,毫秒 myTimer.Elapsed += new ElapsedEventHandler(WriteChar);//Elapsed事件该事件必须匹配System.Timers.ElapsedEventHandler委托类型的返回值 //处理程序和事件订阅起来,事件处理方法初始化为一个新的委托实例 //可以直接写成myTimer.Elapsed +=WriteChar; myTimer.Start();//启动定时器 System.Threading.Thread.Sleep(200);//将当前线程阻塞指定的毫秒数 Console.ReadKey(); } static void WriteChar(object source, ElapsedEventArgs e) { Console.Write(displayString[counter++ % displayString.Length]); } } System.Timers.ElapsedEventHandler委托类型是标准委托之一, void <MethodName>(object source,ElapsedEventArgs e)
定义事件
public delegate void MessageHandler(string messageText);//定义委托,该委托用于定义的事件必须指明返回值的参数值 public class Connection { public event MessageHandler MessageArrived;//给时间命名并指定委托的类型 private Timer pollTimer;//定时器 public Connection() { pollTimer = new Timer(100);//定时器时间 pollTimer.Elapsed += new ElapsedEventHandler(CheckForMessage);//定时器的事件委托 } public void Connect() { pollTimer.Start();//定时器开始 } public void Disconnect() { pollTimer.Stop();//定时器结束 } private static Random random = new Random(); private void CheckForMessage(object source, ElapsedEventArgs e)//定时器的委托函数; { Console.WriteLine("Checking for new messages."); if ((random.Next(9) == 0) && (MessageArrived != null))///生成一个0-9的数,如果是0,并且事件有订阅者,则事件实现委托就使用委托来 { MessageArrived("Hello Mum!"); } } }
public class Display { public void DisplayMessage(string message) { Console.WriteLine("Message arrived: {0}", message); } }
class Program { static void Main(string[] args) { Connection myConnection = new Connection(); Display myDisplay = new Display(); myConnection.MessageArrived +=new MessageHandler(myDisplay.DisplayMessage);//将委托和函数绑定 myConnection.Connect(); System.Threading.Thread.Sleep(200); Console.ReadKey(); }
多用途的事件处理程序
Timer.Elapsed事件的委托包含了事件处理程序中常见的两类参数 object source:引发事件对象的引用 ElapsdEventArgs:由时间传送的参数 由不同对象引发的几个相同事件使用相同事件处理程序
public class MessageArrivedEventArgs : EventArgs//定义消息类 { private string message; public string Message { get { return message; } } public MessageArrivedEventArgs() { message = "No message sent."; } public MessageArrivedEventArgs(string newMessage) { message = newMessage; } }
public class Connection { public event EventHandler<MessageArrivedEventArgs> MessageArrived;//事件,EventHandler<T>为委托模板,将消息类传入 private Timer pollTimer; public string Name { get; set; } public Connection() { pollTimer = new Timer(100); pollTimer.Elapsed += new ElapsedEventHandler(CheckForMessage); } public void Connect() { pollTimer.Start(); } public void Disconnect() { pollTimer.Stop(); } private static Random random = new Random(); private void CheckForMessage(object source, ElapsedEventArgs e) { Console.WriteLine("Checking for new messages."); if ((random.Next(9) == 0) && (MessageArrived != null)) { MessageArrived(this, new MessageArrivedEventArgs("Hello Mum!"));//发送消息 } } }
public class Display//事件响应函数 { public void DisplayMessage(object source, MessageArrivedEventArgs e) { Console.WriteLine("Message arrived from: {0}", ((Connection)source).Name); Console.WriteLine("Message Text: {0}", e.Message); } }
class Program { static void Main(string[] args) { Connection myConnection1 = new Connection(); myConnection1.Name = "First connection."; Connection myConnection2 = new Connection(); myConnection2.Name = "Second connection."; Display myDisplay = new Display(); myConnection1.MessageArrived += myDisplay.DisplayMessage; myConnection2.MessageArrived += myDisplay.DisplayMessage; myConnection1.Connect(); myConnection2.Connect(); System.Threading.Thread.Sleep(200); Console.ReadKey(); } }
.net提供了两个委托类型:EventHandler和EventHandler<T>
匿名方法,纯粹是为了用作委托目的而创建的
delegate(parameters) { } parameters参数化列表
myConnection1.MessageArrived += delegate(Connection source,MessageArriveEventArgs e) {Console.writeline("message arrived from{0}",source.Name);}
特性:可以为代码段标记一些信息 这些信息可以从外部读取
[DebuggerStepThrough] public void DullMethod() {}该特性说明,在调试的时候不进入该方法进行逐句调试,而是跳过该方法 该特性通过DebuggerStepThroughAttribute这个类来实现的,这个类位于System.Diagnostics名称空间中 特性的参数可以自己设置[DoesInterestingThings(1000, WhatDoesItDo = "voodoo")]
读取特性需要用到反射 Type.GetCustomAttributes来实现
改进
对象初始化器 省略构造函数的括号,自动调用无参数构造函数,之后调用初始化器 同时对象初始化器还可以进行嵌套
//初始化器,使用默认构造函数实现赋值 <classname> <variableName>=new <claseename> { <propertyOrField1>=<value1>, <propertyOrField2>=<value2>, <propertyOrField3>=<value3>, ... } Curry tastyCurry=new Curry { MainInte="ddddd"; Origin=new restaurant { Name="ddd"; } };
集合初始化器
类型推理:var ,编译器来确定类型,var关键字还可以通过数组初始化器来推断数组的类型: var myarray=new [] {4,5,6,7}; 4,5,6,7必须遵循:相同的类型,相同的引用类型或空,所有元素的类型都可以隐式转换为同一类型
匿名对象:为了减少创建对象所消耗的时间,其理念就是让编辑器根据要存储的数据自动创建类型,而不是定义简单的数据存储类型 匿名类型 var curry=new { MainIngredient="lamb", Style="Dhansnk", Spiciness=5 }; 1,使用var关键字,因为匿名类型没有可以使用的标识符, 2,new后面没有指定类型的名称 匿名对象的属性被定义为只读,如果在存储对象中修改属性的值,就不能使用匿名对象。 使用动态查找功能可以处理未知的C#类型
class Program { static void Main(string[] args) { var curries = new[] { new { MainIngredient = "Lamb", Style = "Dhansak", Spiciness = 5 }, new { MainIngredient = "Lamb", Style = "Dhansak", Spiciness = 5 }, new { MainIngredient = "Chicken", Style = "Dhansak", Spiciness = 5 } }; Console.WriteLine(curries[0].ToString()); Console.WriteLine(curries[0].GetHashCode()); Console.WriteLine(curries[1].GetHashCode()); Console.WriteLine(curries[2].GetHashCode()); Console.WriteLine(curries[0].Equals(curries[1]));//比较状态,即每个属性的值 Console.WriteLine(curries[0].Equals(curries[2])); Console.WriteLine(curries[0] == curries[1]); Console.WriteLine(curries[0] == curries[2]); Console.ReadKey(); } }
{ MainIngredient = Lamb, Style = Dhansak, Spiciness = 5 } 294897435 294897435 621671265 True False False False
动态类型:dynamic myDynamicVar;在编译期间会被object替代,
默认参数类型
可变参数
命名参数:首先选定必选参数,再指定命名的可选参数
public static List<string> GetWords( string sentence, bool capitalizeWords = false, bool reverseOrder = false, bool reverseWords = false) { List<string> words = new List<string>(sentence.Split(' ')); if (capitalizeWords) words = CapitalizeWords(words); if (reverseOrder) words = ReverseOrder(words); if (reverseWords) words = ReverseWords(words); return words; } words = WordProcessor.GetWords( sentence, reverseWords: true, capitalizeWords: true);
扩展方法 扩张方法可以扩展类型的功能而不修改类型本身
要创建和使用扩展方法必须: 1,创建一个非泛型静态类 2,使用扩展方法的语法,为所创建的类添加扩展方法,做为静态方法 3,确保使用扩展方法的代码用using语法导入包含扩展方法类的名称空间 4,通过扩展类型的一个实例调用扩展方法,与调用扩展类型的其他方法一样。
扩展方法的要求: 1,方法必须是静态的 2,方法必须包含一个参数,表示调用扩展方法的类型实例 3,实例参数必须是方法定义的第一个参数, 4,除了this关键字外,实例参数不能有其他修饰符
public static class ExtensionClass { public static <ReturnType> <ExtensionMethodName>(this <TyprToExtend> instance ,<otherParamters>) { ... } }
public static class ExtensionClass { public static <ReturnType> <ExtensionMethodName>(this <TyprToExtend> instance ,<otherParamters>) { ... } } <TypeToExtend> myVar; //myVar is initialized by code not shown here myVar.<ExtensionMethodName>(); <TypeToExtend> myVar; ExtensionClass.<ExtensionMethodName>(myVar); 导入后可以通过IntelliSense查看扩展方法 定义了一个扩展方法后还可以将其运用到派生于这个类型的子类型中
namespace ExtensionLib { public static class WordProcessor { public static List<string> GetWords( this string sentence, bool capitalizeWords = false, bool reverseOrder = false, bool reverseWords = false) { List<string> words = new List<string>(sentence.Split(' ')); if (capitalizeWords) words = CapitalizeWords(words); if (reverseOrder) words = ReverseOrder(words); if (reverseWords) words = ReverseWords(words); return words; } public static string ToStringReversed(this object inputObject) { return ReverseWord(inputObject.ToString()); } } } using ExtensionLib; static void Main(string[] args) { Console.WriteLine("Enter a string to convert:"); string sourceString = Console.ReadLine(); Console.WriteLine("String with title casing: {0}", sourceString.GetWords(capitalizeWords: true) .AsSentence()); Console.WriteLine("String backwards: {0}", sourceString.GetWords(reverseOrder: true, reverseWords: true).AsSentence()); Console.WriteLine("String length backwards: {0}", sourceString.Length.ToStringReversed()); Console.ReadKey(); }
lambda表达式
1,定义一个事件处理方法,其返回类型和参数匹配要订阅的事件需要委托返回类型和参数 2,声明一个委托类型的变量 3,把委托变量初始化为委托类型的实例,实例指向事件处理方法 4,把委托变量添加到时间的订阅者列表中
正常的事件 Timer myTimer =new Timer(1000); myTimer.Elapsed+=new ElapsedEvendHandler(WriteChar); //可以直接写成 myTimer.Elapsed+=WriteChar; //使用匿名的方法 myTimer.Elapsed+= delegate(object source,ElapsedEvendArgs e) { Console.WriteLine("Event handler called after {0} milliseconds"), (source as Timer).Interval); }
//使用lambda表达式 myTimer.Elapsed+=(source ,e )=>Console.WriteLine( "Event handler called after {0} milliseconds", (source as Timer).Interval); //放在括号中的参数列表(未类型化) //=>运算符 //C#语法
使用lambda表达式的例子
delegate int TwoIntegerOperationDelegate(int paramA, int paramB); class Program { static void PerformOperations(TwoIntegerOperationDelegate del) { for (int paramAVal = 1; paramAVal <= 5; paramAVal++) { for (int paramBVal = 1; paramBVal <= 5; paramBVal++) { int delegateCallResult = del(paramAVal, paramBVal); Console.Write("f({0},{1})={2}", paramAVal, paramBVal, delegateCallResult); if (paramBVal != 5) { Console.Write(", "); } } Console.WriteLine(); } } static void Main(string[] args) { Console.WriteLine("f(a, b) = a + b:"); PerformOperations((paramA, paramB) => paramA + paramB); Console.WriteLine(); Console.WriteLine("f(a, b) = a * b:"); PerformOperations((paramA, paramB) => paramA * paramB); Console.WriteLine(); Console.WriteLine("f(a, b) = (a - b) % b:"); PerformOperations((paramA, paramB) => (paramA - paramB) % paramB); Console.ReadKey(); } } 结果: f(a, b) = a + b: f(1,1)=2, f(1,2)=3, f(1,3)=4, f(1,4)=5, f(1,5)=6 f(2,1)=3, f(2,2)=4, f(2,3)=5, f(2,4)=6, f(2,5)=7 f(3,1)=4, f(3,2)=5, f(3,3)=6, f(3,4)=7, f(3,5)=8 f(4,1)=5, f(4,2)=6, f(4,3)=7, f(4,4)=8, f(4,5)=9 f(5,1)=6, f(5,2)=7, f(5,3)=8, f(5,4)=9, f(5,5)=10 f(a, b) = a * b: f(1,1)=1, f(1,2)=2, f(1,3)=3, f(1,4)=4, f(1,5)=5 f(2,1)=2, f(2,2)=4, f(2,3)=6, f(2,4)=8, f(2,5)=10 f(3,1)=3, f(3,2)=6, f(3,3)=9, f(3,4)=12, f(3,5)=15 f(4,1)=4, f(4,2)=8, f(4,3)=12, f(4,4)=16, f(4,5)=20 f(5,1)=5, f(5,2)=10, f(5,3)=15, f(5,4)=20, f(5,5)=25 f(a, b) = (a - b) % b: f(1,1)=0, f(1,2)=-1, f(1,3)=-2, f(1,4)=-3, f(1,5)=-4 f(2,1)=0, f(2,2)=0, f(2,3)=-1, f(2,4)=-2, f(2,5)=-3 f(3,1)=0, f(3,2)=1, f(3,3)=0, f(3,4)=-1, f(3,5)=-2 f(4,1)=0, f(4,2)=0, f(4,3)=1, f(4,4)=0, f(4,5)=-1 f(5,1)=0, f(5,2)=1, f(5,3)=2, f(5,4)=1, f(5,5)=0
lambda表达式的参数,参数可以指定类型,但是不能一个指定,另一个不指定 (int paramA,int paramB)=>paramA+paramB lambda表达式的语句体 (param1,param2)=> { //Multiple statements ahoy } (param1,param2)=> { return returnValue; }
lambda有两种理解: lambda表达式是一个委托,可以将其赋给一个委托类型的变量, Action,表示lambda表达式不带参数,返回类型void Action<>,表示lambda表达式最多8个参数返回类型是void Func<>,表示lambda表达式最多8个参数,返回类型不是void lambda表达式是表达式树, LINQ框架包含一各泛型Expression<>,可以转换为封装的Lambda表达式
lambda表达式和集合 学习Func<>委托后就可以理解,System.Linq名称空间中的为数组提供的扩展方法 public static TSource Aggregate<TSource>( this Enumerable<TSource> source, Func<TSource,TSource,TSource> func); public static TAccumlate Aggregate<TSource,TAccumlate>( this Enumerable<TSource> source, TAccumlate seed, Func<TAccumlate,TSource,TAccumlate> func); public static TResult Aggregate<TSource,TAccumlate,Aggregate<TSource,TAccumlate,TResult> (TResult>(this IEnumerable<TSource> source, TAccumlate seed, Func<TAccumlate,TSource,TAccumlate> func, Func<TAccumlate,TResult>resultSelectoe); 这是一个累计器 int[] myIntArray={1,2,3,4}; int result = myIntArray.Aggregate(...); int result = myIntArray.Aggregate<int>(...); int result = myIntArray.Aggregate((paramA,paramB)=>paramA+paramB);
class Program { static void Main(string[] args) { string[] curries = { "pathia", "jalfrezi", "korma" };//6,8,5 Console.WriteLine(curries.Aggregate( (a, b) => a + " " + b)); Console.WriteLine(curries.Aggregate<string, int>( 0, (a, b) => a + b.Length)); Console.WriteLine(curries.Aggregate<string, string, string>( "Some curries:", (a, b) => a + " " + b, a => a)); Console.WriteLine(curries.Aggregate<string, string, int>( "Some curries:", (a, b) => a + " " + b, a => a.Length)); Console.ReadKey(); } } pathia jalfrezi korma 19 Some curries: pathia jalfrezi korma 35
调用方信息特征
CallFilePath()该特性用于获取调用代码所在的文件路径 CallerLineNumber()该特性用于获取调用代码在文件中的行号 CallMemberName()获取调用代码所在成员的名称
class Program { static void Main(string[] args) { DisplayCallerInformation(); Action callerDelegate = new Action(() => DisplayCallerInformation()); callerDelegate(); CallerHelper(callerDelegate); Caller(); DisplayCallerInformation(3, "Bob", @"C:\Temp\NotRealCode.cs"); Console.ReadKey(); } static void CallerHelper(Action callToMake) { callToMake(); } static void Caller() { DisplayCallerInformation(); } static void DisplayCallerInformation( [CallerLineNumber] int callerLineNumber = 0, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "") { Console.WriteLine( string.Format( "Method called from line {0} of member {1} in file {2}.", callerLineNumber, callerMemberName, callerFilePath)); } }