基本信息
CIL和JIT
- CIL通用中间语言
- JIT just-in-time使得CIT代码仅在需要时才编译
程序集
- 包含可执行文件.exe和库函数.dll和资源文件,不必把程序集集中到一个地方,全局程序缓存
- 程序集完全自描述的,逻辑单元而不是物理单元
- 可执行代码和库代码使用相同程序集结构,可执行文件多了一个主程序入口点。
- 程序集的一个重要特征是它们包含元数据描述了对应代码中定义的类型和方法。
CLR
公共语言运行库CLR:包含实时编译器JIT,在程序运行时,JIT编译器会从IL代码生成本地代码,其他部分是垃圾回收器GC,调试器扩展和线程实用工具。垃圾回收器负责回收内存,调试扩展器允许在不同编程语言之间启动调试会话,线程实用工具负责在底层平台创建线程。
- 管理着正在执行的代码包括管理内存,处理安全以及跨语言调试
- 代码托管最重要的是内存回收机制
- winform基于像素
- wpf基于pirectX
CLR执行应用程序之前,编写好源代码都需要编译,编译分为两个阶段
- 将源代码编译成microsoft中间语言IL
- CLR将I编译成平台专用的的本地代码
clr还有一个类型加载器的类型系统,类型加载器负责从程序集中加载类型。
编译过程
- .net兼容语言编写程序,托管语言
- 将代码编译为中间代码CIL,这未必是单文件,可以有多个源代码文件,再把链接带一个程序集中,称之为链接
- 使用JIT编译为本机代码
- 在托管的CLR环境下运行本机代码
基本语法
注释
- /* */和一行//
- ///可以通过配置,将这些注释提取出来组成文档文件
命名空间
代码大纲功能
- #region Using directives
- #endregion,大纲的名字为Using directives
变量
- 先声明后使用
变量初始化:变量是类或结构中的字段,如果没有显式初始化,则创建这些变量时,其默认值就是0.方法的局部变量必须在代码中显示初始化。
- 整数
- 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位数字
- 浮点类型
- 位是最小数据单位,只能表示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个字节
- 布尔和文本
- char System.Char Unicode 0~65535 2个字节
- bool System.Boolean 1个字节
- string System.String
- 转义字符
- ' 单引号 0x0027
- '' 双引号 0x0022
- \ 反斜杠 0x005c
- \0 空 0x0000
- \a 警告 0x0007
- \b 退格 0x0008
- \f 换页 0x000C
- \n 换行 0x0000A
- \r 回车 0x000D
- \t 水平制表符 0x0009
- \v 垂直制表符 0x000B
- @转义字符,避免大量使用
全局变量:Program.全局变量,在声明变量前要进行初始化
- 常量:
- 常量必须初始化,指定值后不能更改。
- 常量总是隐式静态的,
- 值类型和引用类型:值类型存储在栈中,应用类型存储在堆中。值类型和引用类型互换要经过装箱拆箱,在传递函数参数时,值类型会进行复制,而引用相当于传递指针,返回值相同。引用类型由垃圾回收器进行回收,值类型不需要,超出其作用域就会在内存中删除。
- ref:如果是结构类型使用ref传递参数,则变成传递引用,但如果是引用类型,即使参数在函数中变化了引用,新的应用还是会传递回去。ref传递的值要先初始化。
- out:用法基本和ref一样,但传入的值只需要定义
- 可空类型:int? 和int,唯一的多开销是一个可以确定它是否为空 的布尔成员,值可以直接转换可空,int?=int;可空转换成值需要强制int=(int)int?;但如果为空会生成一个异常,最好的方法是int=int?.hasvalue?int?.value:-1;可以转换成较短短语int=int??-1
- 枚举也是值类型:默认情况下,枚举是int,也可以改变成其他整数类型,强制转换将int转换成枚举,当分配给常量是不同位时,flags属性需要枚举设置,获得所有枚举,var day in Enum.GetName(typeof(Color));
结构:值类型,不能继承,每个结构都自动由ValueType派生。
基本运算符
- 一目运算符
- 二目运算符
- =
- +=
- -=
- *=
- /=
- %=
- 运算符优先级
- ++,--(前缀);(),+,-,!,~
- *,/,%
- +,-
- >> ,<<
- <,>,<=,>=
- ==,!=
- &
- ^
- |
- &&
- ||
- =,+=,-=,*=,/=,%=
- ++,--后缀
- 布尔运算符
- !
- &
- |
- ^一真一否才为真
基本语句
goto语句
goto
:- if语句
if()
{
;
}
else
{
;
}
- switch
switch()
{
case:
>==>
break;
........
default:
>!=>
break;
}
- do
do
{
} while()
- while
while()
{
}
- for
for(;;)
{
}
- foreach
foreach(in)
{
//can use
- 循环中的中断
- 循环的中断:
- break立刻终止循环
- continue立刻终止当前循环
- goto可以跳出循环
- return跳出循环,即包含循环的函数
基本数据结构
- 转换
- 隐式转换:编译器执行转换
- 显示转换:需要额外转换
(
) - 显示转换的检查:checked和unchecked
- checked(
)检查是否会溢出,溢出则出现错误 - unchecked(
)溢出不出现错误
可以通过设置属性,使得显示转换都默认检查checked
- 枚举
enum:
{
,
=,
...
}
- 结构体:值类型
struct:
{
,
...
}
- 数组
- 数组声明:
[] ; - 二维数组声明:
[,] ; - 数组调用:
frendName[index]
- 锯齿数组
- 数组声明:
基本函数
- Console
- Console.WriteLine("字符串");
- Console.WriteLine("{0}{1}{2}",n_a,n_b,n_c);
- str=Console.ReadLine();
- Convert
- n_double=Convert.ToDouble();强制转换成double
- 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 位无符号整数。
- Convert.ReadKey( );
- enum
(enumerationType)Enum.Parse(typeof (enumerationType),enumerationValueString);
字符串转换成枚举
- string
- .ToCharArray()这个函数可以将String编程char[]数组
- .Length字符串长度
- .ToLower字符串全部小写
- .ToUpper字符串全部大写
- .Trim字符串删除空格
- .PadLeft()和.PadRight()字符串左边或者右边添加空格
- .IndexOf(',');定位到,的下标
- .Split(' ');切片
- object
- 类的函数基本是object函数
- .ToString()输出类名;
- 函数格式
static (,...)
{
...
renturn ;
}//普通函数定义
可以直接定义表达式函数public bool IsSquare(Reactangle rect)=>rect.height==rect.width;
- 参数函数
- 参数数组,C#允许函数指定一个特殊参数,该参数必须是函数的最后一个,称为参数素组,
static
( ,...Params [] ) { ... renturn ; } 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); } }
- 可选参数
public void TestMethod(int i,int j=12)
{
}
- 引用参数
- 引用参数,需要用关键字ref
- 调用引用函数
(ref )
static
(ref ,...) { ... renturn ; } 扩展方法
public static int GetWordCount(this string s)=>s.split().length;
调用int wordcount=fox.GetWordCount();
- 函数重载
- 特征标不一样就行
- 引用和非引用也属于重载
- 委托
- 委托,和重载差不多,只不过重载特征表不同,而委托特征标相同而函数名不同,可以根据需求NEW出不同的函数,委托的利用在于可以将其当成参数传递给函数,让函数因需求执行不同的操作,类似于多态
- 委托是一种存储函数引用的类型
- 委托声明类似于函数,但是不带函数体,且要使用关键字delegate,委托指定了一个返回类型和一个参数列表
- 定义了委托后,就可以声明该委托类型的变量,接着把这个变量初始化为与委托具有相同返回类型和参数列表的函数引用,之后就可以使用委托变量调用这个函数
- 例子,可以把委托变量作为参数传递给一个函数,这样,该函数就可以使用委托调用它引用的任何函数
class Program { delegate double ProcessDelegate(double param1, double param2); static double Multiply(double param1, double param2) { return param1 * param2; } static double Divide(double param1, double param2) { return param1 / param2; } static void Main(string[] args) { ProcessDelegate process; Console.WriteLine("imput two intege:"); string input = Console.ReadLine(); string [] commaPos = input.Split(','); double param1 = Convert.ToDouble(commaPos[0]); double param2 = Convert.ToDouble(commaPos[1]); Console.WriteLine("Enter M to Multiply or D to divide:"); input = Console.ReadLine(); if (input == "M") process = new ProcessDelegate(Multiply);//process=Multiply; else process = new ProcessDelegate(Divide);//process=Divide; Console.WriteLine("result :{0}", process(param1, param2)); Console.ReadKey(); } }
调试
断点
- 可以查看输出窗口,输出/调试,
- 语句Debug.WriteLine()将调试信息显示到调试窗口
- Trace.WriteLine(),用法相同,可以用于发布
- Debug.WriteLine("Add 1 to i","MyFunc");结果为:
- MyFunc:Add 1 to i;
- using System.Diagnostics;
- Debug.WriteLineIf()增加一个必选项,当是真的时候进行输出
跟踪点
- 有点类似于Debug.WriteLine()
Trace
Trace.Assert(myVar<0,"variable out of bounds","please contact vendor with the error code KCW001.")
异常
- try抛出异常,catch抛出异常时执行的代码,finally包含最终执行的代码
- 即使try中有return, finally还是会执行,在finally中还是会改变return中的值
try
{...}
catch(e)
{...}
finally
{...}
- 抛出异常包括内容
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);
}
面向对象编程
类包括:字段/属性/方法/常量/构造函数/索引器/运算符/事件/类型/析构函数
字段:不应该被设置为public
属性:自动实现的属性也就是没有声明私有字段,就可以使用初始化器来初始化;public int Age{set;get;}=42;
set和get可以被访问修饰符修饰,但get和set中必须有一个具有属性的访问级别,属性其实并不怎么消耗资源,因为在JIT中,被变成内联函数,
readonly:readonly只能在构造函数中赋值,然后就不好修改。
构造函数
- 静态构造函数
- 创建包含静态构造函数的类实例
- 访问包含静态构造函数的类的静态成员时,(无论创建多少个实例,静态构造函数只调用一次。)
- 构造函数和析构函数,和C++一样
- 静态类不能实例化对象,只能包含静态成员,
- 如果没有构造函数,编译器会自动添加一个默认构造函数
派生类构造函数的调用
构造函数总是按照层次结构顺序调用的:先调用object的构造函数,然后从上到下一次调用,直到达到编译器要实例化的类为止
如果要调用基类的非默认构造函数就需要使用构造函数初始化器。
访问修饰符
public/ 所有类型成员/任何代码均可以访问该项
protected/类型和内嵌类型的所有成员/只有派生类型能够访问该类
internal/所有类型和成员/只能包含他的程序集中能够访问该类
private/类型和内嵌类型的所有成员/只能在它所属的类型中访问该项
protected internal/类型和内嵌类型的所有成员/只能在包含他的程序集和派生类型的任何代码中访问该项
new/static /virtual/abstract/override/sealed/extern
字段和属性
- 统一建模语言Unified Modeling Language(UML)
接口
- 接口可以看成是类的引用,可以引用任何实现该接口的类
- 一般情况下接口只能包含:方法,属性,索引器,事件的声明
- 把公共实例(非静态)方法和属性组合起来,以封装特定功能的一个集合,接口不能实例化
- IDisposable接口的对象必须实现其Dispose()方法,当不再需要某个对象时就调用这个方法,释放重要资源,
- C#简化了这种方法,using关键字可以在代码块中初始化使用重要资源的对象,这个代码块的结尾会自动调用Dispose()方法
=new ();
...
using()
{
...
}
或
using(=new ())
{...}
- 接口可以彼此集成,和类的集成差不多
继承
- C#类可以派生自另一个类和多个接口,只有虚方法和抽象方法才能使用override重写,不然是不能重写的,如果不重写虚方法或抽象方法,需要使用关键字new,那么调用的时候调用子类方法就可以使用base.方法
- 纯虚基类,重写方法,也可以不重写,虚方法可以实现,也可以实现(同属性)
- 基类成员的访问性 protected\privated\public
- 抽象基类不能实例化,抽象基类必须被继承,成员没有实现代码,在派生类中实现他们
- 抽象基类在UML中为斜体
is and as
IBankAccount account= o as IBankAccount;
if(o is IBankAccount){IBankAccount account = (IBankAccount)o;}
多态
- 通过实例化不同的派生类调用派生类的重写方法
- 将派生类赋给基类调用基类重写方法,实际上调用了基类的重写方法
- 接口的多态性,尽管不能实例化接口,可以建立接口类型的变量,然后就可以在支持该接口的对象上,使用这个变量来访问接口提供的方法和属性
- 例如不适用基类提供的方法,而是把该方法放在接口上,实例化也支持该接口,唯一的区别是方法的实现代码不一样(接口不包含方法的实现),通过接口可以实现访问多个对象的方法而不依赖于一个公共的基类
Cow myCow=new Cow(); Chicken myChicken=new Chicken(); IConsume consumeInterface; consumeInterface = myCow; consumeInterface.EatFood(); consumeInterface = myChicken; consumeInterface.EatFood();
- 对象之间的关系
- 包含关系,这个成员字段可以是公共字段,此时是继承关系一样,容器对象的用户就可以访问它的方法和属性,与继承不同的是,不能访问类的内部代码,类变成其私有变量
- 集合关系,增加了功能的数组
密封类和密封方法
如果不允许创建派生自某个自定义类的类,该自定义类就应该是密封的,sealed
- 使用密封类可以提高性能,编译器知道该类没有派生,因此就没有虚函数表
- string是密封的
托管和非托管
托管和非托管资源,存储在托管或本机堆中的对象,垃圾回收器释放存储在托管堆中的对象,却不会释放本机堆中的对象。
值数据类型:window使用虚拟寻址系统,可以将程序所使用的内存地址映射内存中的实际地址(编译器来做)。每个进程都会有4G的内存,该内存被称为虚拟内存,从0开始往下排。在虚拟内存中有一个地方叫做栈,栈存储的是不是对象成员的值数据类型,调用方法时,栈存储传递给方法的所有参数副本。
栈实际上是向下填充的,先0x800000,然后0x799999
int a=10;
double b=7999;
在运行到a=10时,int是四个字节,0x799999-0x799996被10占用,下一个空闲单元是0x799995,double 8个字节,就是0x799987 超出b的定义域后就会加8个字节,超出a的定义域后就会加4个字节。
ab进入栈的顺序是由编译器来决定的,编译器会查看作用域的顺序来决定哪个先进栈哪个后进栈。
引用类型的堆:堆上的内存是向上分配的
void Work()
{
Customer arabel;//1
arabel= new Customer();//2
Customer othercustomer2=new Customer();//3
}
首先1声明一个customer的引用arabel,在栈上给这个引用分配存储空间,该引用为4个字节,
2分配堆上的内存,以存储一个真正的Customer对象,然后把变量arabel的值设置为新分配的customer对象的内存地址,该customer可能包含32个字节,类的字节数和类的字段等成员有关
3是声明一个customer的引用,放在栈上,实例化一个customer对象放在堆上,将堆上的地址放到栈的引用上。
非托管堆:
当一个引用变量超出作用域时,它的引用会从栈中删除,但引用的对象任然在堆中,一直到程序终止,或垃圾回收器回收他们。
垃圾回收,删除堆中不再有被引用的所有对象,垃圾回收器(C++垃圾回收)在引用跟表中找到所有引用对象,在引用对象树中查找,在完成删除后,堆会立即把对象分散开来,与已经释放的内存混在一起。
托管堆:
如果托管堆也是这样,其给新对象分配内存时就是一个很难处理的问题,运行库必须遍历整个堆,才能找到足够大内存来存储新的对象。但垃圾回收器不会让堆处在这种状态,它在释放完可以释放的资源后,会将其他对象回推到堆的底部,再次形成连续数据块,在移动时所用引用都要更新,这需要垃圾回收器来完成。
垃圾回收期的压缩操作就是托管堆和非托管堆的区别,托管堆虽然要压缩,但分配内存时,只需要地址就好,不需要遍历地址列表。
创建对象时,会把对象放到托管堆上称为0代,创建新对象会被移动到这一部分,第一次回收后,保留下来的内容会被压缩移动到堆的下一部分上或世代部分,第1代对应部分,此时第0代对应的部分为空,新的对象依然被放到这个部分,遗留下来的内容让在第1代部分,第二次回收,第1代被压缩到第2代部分,第0代被压缩到第1代,第0代为空,放新的内容。如果新的对象超出第0代部分就会进行垃圾回收
垃圾回收可以提供应用程序性能,可以在架构堆上处理较大对象的方式,
.NET较大对象有自己的堆,称为大对象堆,对象大于85000个字节就会被放在这个堆上,而不是主堆上,因为较大对象的压缩代价比较大,所以不放在主堆上压缩
第二代和大对象堆的回收即压缩过程是在后台线程中进行的
- 强引用和弱引用
仍在引用的对象的内存为强引用,可以回收不在根表中直接或间接引用的托管内存
A引用B,B引用C,C引用A,则GC会销毁所有对象。
实例化一个类,只要有代码引用它就是强引用
var myclassVariable = new MyClass();
var myCache=new MyCache();
myCache.Add(myclassVariable);
myclassVariable=null//3
当第3步运行时就不能释放myclassVariable所引用的内存,因为缓存中还存在引用。在弱引用中就可以避免这种现象。
弱引用可以创建和使用对象,但是如果垃圾回收器在运行就可以回收
弱引用是由WeakReference创建的。需要使用IsAlive属性来确认是否被回收,Target属性可以返回一个强引用,不为null就可以访问
var myWeakReference = new WeakRefernece(new DataObject());
if(myWeakReference.IsActive)
{
DataObject strongReference=myWeakReference.Target as DataTarget;
if(strongReference!=null)
{}
}
else
{}
垃圾回收器不释放非托管资源
比如:文件句柄,网络连接,数据库连接,等
定义类时,可以有两种机制来自动释放非托管资源
- 声明一个析构函数(或则终结器),作为类成员
class MyClass
{
~MyClass()
{
}
}
编译器在编译析构函数时,会隐式把析构函数的代码编译为等价于重写Finalize()方法的代码,从而确保父类的Finalize()会被执行
等价
protected override void Finalize()
{
try
{}
finally
{
base.Finally();
}
}
C#析构函数具有不确定性,无法确定析构函数会被何时执行,同时析构函数会延迟对象从内存中删除的时间,有析构函数两次才能删除对象,第一次调用析构函数,第二次调用才真正删除,同时会被另起一个线程来调用Finally()
- 实现System.IDisposable 接口
该接口替代析构函数
class MyClass:IDisposable
{
public void Dispose()
{
}
}
Dispose()方法的实现显示释放由对象直接使用的所有非托管资源。Dispose为何时释放非托管资源提供了精确的控制。
using语句会自动调用Dispose()方法
上面的两种方法都实现了释放非托管资源
下面是一个双重实现的代码:
using System;
public class ResourceHolder:IDisposable
{
private bool _isDisposed=false;
public void Dispose()
{
DisPose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if(!_isDisposed)
{
if(disposing)
{
//cleanup managed objects by calling their dispose() methods
}
//clean up unmanaged objects
}
_isDisposed = true;
}
~ResourceHolder()
{
Dispose(false);
}
public void SomeMethod()
{
//enture object not already disposed before execution of any method
if(_isDisposed)
{
throw new ObjectDisposedException("ResourceHolder");
}
//method implementation
}
}
运算符重载
事件
- 对象可以激活和使用事件,作为他们处理的一部分,事件非常重要,可以在代码的其他部分起作用,类似于异常,但是功能更强大
- 可以在Animal对象添加到Animal集合中,执行特性的代码,而这部分代码不是Animal类的一部分,也不是调用Add()方法的代码的一部分,为此,需要给代码添加事件处理程序,这是一种特殊类型的函数,在事件发生时调用,还需要配置这个处理程序,以监听自己感兴趣的事情
private void Button1_Click_1(object sender, RoutedEventArgs e) { ((Button)sender).Content = "clicked"; Button newButton = new Button(); newButton.Content = "NEW Button"; newButton.Margin = new Thickness(10, 10, 200, 200); newButton.Click += newButton_click; ((Grid)((Button)sender).Parent).Children.Add(newButton); } private void newButton_click(object sender, RoutedEventArgs e) { ((Button)sender).Content = "clicked"; }
引用类型还是值类型
- 在C#中,数据根据变量的类型以两种方法中的一种存储在一个变量中:
- 值类型的内存的同一个地方存储它们和它们的内容,栈
- 引用类型存储指向内存中其他某个位置的引用,实际内容存储在这个位置,堆
定义类
- 类声明为内部,只有当前项目的代码能够访问 internal(默认)
其他项目代码也能访问public - 类是抽象的,不能实例化,只能继承abstract,密封的sealed不能继承,如果基类是抽象的,在派生类必须全部实现
- 无或internal 只能在当前项目中访问
- public可以在任何地方访问类
- abstract或internal abstract类只能在当前项目访问,不能实例化,只能被继承
- public abstracrt类可以在任何地方访问,不能实例化,只能继承
- sealed或internal sealed类只能在当前项目访问,不能被继承,只能实例化
- public sealed类可以在任何地方访问,不能被继承只能实例化
public sealed class MyClass { //Class members }
- 派生类的可访问性不能高于基类
- 类还可以指定接口,同时必须实现该接口的所有成员,如果不想使用给定的接口成员,就可以提供一个空的实现方法,继承类(只能有一个)再继承接口,接口可以有多个
- 接口的定义,不能在接口中使用abstract或sealed,可以使用继承的方式继承接口
interface IMyInterface
{
....
}
- 所有类都隐式继承System.Object类
- 多态性便可以使用,比较重要的基类方法GetType()ToStrign()
if(myObject.GetType()==typeof(MyComplexClass)) { }
- 初始化列表
- :base(i)
- :this(5,6)调用自己的两个参数的构造函数,可以伪装成默认构造函数
定义类成员
- public,private,internal,protected
- readonly表示字段只能在执行构造函数的过程中赋值,static 静态变量,const也是静态的
- 方法
- virtual方法可以重写
- abstract方法必须在非抽象的派生类中重写(只用于抽象类中)
- override方法重写了一个基类方法(如果方法被重写,就必须要使用关键字),如果使用了override,也可以使用sealed来指定在派生类中不能对这个方法做进一步修改即这个方法不能由派生类重写
- extern方法定义在其他地方
- 多态测试,使用virtual和override多态测试成功
- 属性
- 定义与字段类似,get和set关键字来定义,可以控制属性的访问级别
- 右击refactor(重构)-Encapsulate Field可以快熟添加属性
- 自动属性:
public int MyIntProp { get;set; }
- 按通常的方式定义属性的名称、类型和可访问性,但是没有提供get和set的实现代码,实现代码由编译器提供(私有字段的名字也由编译器提供,将set变成private 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")); } }
- 隐藏
- 隐藏基类方法,会产生2个警告(如果要确定隐藏需要加上new),无论基类怎样限制,只要派生类没有调用override都是影藏
- 要对派生类的用户隐藏继承的公共成员,但仍能访问其功能,要给继承的虚拟成员添加实现代码,而不是简单地用重写的新实现代码替换它,可以使用base关键字:base.DoSomething()
- this最常用的功能是将当前对象实例的引用传递给一个方法,该方法有一个参数是指向基类的,this关键字的另一个常用方法是限定局部类型成员
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"); } }
- 套嵌类型成员
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();
}
}//嵌套类修改只读变量
- 接口
- 接口不会有访问修饰符
- 接口成员没有实现
- 接口没有字段成员(但是接口可以定义自动属性)
- 不能有关键字static/virtual/abstract/sealed来定义接口
- 但接口是可以继承的,如果要隐藏基类成员,需要加关键字new
public interface IMyInterface { void DoSomething(); void DoSomethingElse(); } public class MyClass:IMyInterface { public void DoSomething() { } public void DoSomethingElse() { } }//接口类必须包含接口的所有成员包括匹配的指定签名包括get和set public interface IMyInterface { void DoSomething(); void DoSomethingElse(); } public class MyBaseClass { public void DoSomething() { } } public class MyDerivedClass:MyBaseClass,IMyInterface { public void DoSomethingElse() { } } public interface IMyInterface { void DoSomething(); void DoSomethingElse(); } public class MyBaseClass:IMyInterface { public virtual void DoSomething() { } public virtual void DoSomethingElse() { } } public class MyDerivedClass:MyBaseClass { public override void DoSomethingElse() { } public override void DoSomethingElse() { } }
- 实现接口
MyClass myObj=new MyClass(); IMyInterface myInt=myObj; myInt.DoSomething(); public class MyClass :IMyInterface { void IMyInterface.DoSomething()//显示调用 { } public void DoSomething()//隐式调用 { } } public interface IMyInterface { int MyIntProperty { get; } } public class MyBaseClass:IMyInterface { public int MyIntProperty{get;protected set;} }
- 把接口放在不同的文件中partial
//定义类时使用 public partial class MyClass { } //部分方法定义 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"); } } //如果删除部分方法的实现部分,编译时编译器会以为调用了一个空的部分方法会直接删除部分方法的调用
- 集合
- 可以使用集合来维护对象组
- 集合大多是通过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; } }
- 基类为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循环中,迭代一个collectionObject集合
- foreach
- 调用collectionObject.GetEnumerator(),返回一个IEnumerator引用
- 调用所返回的IEnumerator接口的MoveNext()方法
- 如果MoveNext()返回true.就使用IEnumerator接口的Current属性获取对象的引用,用于foreach循环
- 重复前两步,知道返回false
- 迭代器
- 两种可能返回类型是前面提到的接口类型IEnumerable和IEnumerator
- 如果要迭代一个类,可使用方法GetEnumerator()其返回类型IEnumerator;如果要迭代一个类成员,例如一个方法,则使用Enumerable,
yield return
;
- 深度复制
- 实现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运算符不是用来说明对象是某种类型,而是用来检查对象是不是给定类型,或者是否可以转换成给定类型
is - 如果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 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类型是type类型,operand可隐式转换为type类型,operand可以封箱到type类型,如果operand不能转化为type则表达式结果为nullas
class ClassA:IMyInterface {} class ClassB:ClassA {} ClassA ob1=new ClassA (); ClassD ob2=ob1;//抛出异常 ClassD ob2=ob1 as ClassB;//不会抛出异常但是ob2的值为null;
泛型
泛型是以实例化的过程中提供的类型或类为基础建立的,可以毫不费力的对对象进行强类型化,使用泛型强化为各种类型。与C++不同,C++是在编译期间来强类型化,而C#是在运行期间强类型化
CollectionClass items= new CollectionClass();
items.Add(new ItemClass);
- .net 提供的泛型,包括System.Collections.Generic名称空间
- 可空类型,引用类型可以为空,值类型必须有一个值,扩展值类型让其可为空,泛型使用了System.Nullable类型提供了值类型为空的一种方法
System.Nullable
变量nullableInt可以包含int类型的任意值,还可以拥有nullnullableInt; nullableInt=null;等价于nullableInt=new System.Nullable
(); - nullableInt.HasValue,该方法不适用于引用类型,引用类型本身可能为NULL
int? nullableInt;int?是System.Nullable
的缩写 - 可控类型null+任何数都为null,null在布尔运算中的大小介于FALSE和TRUE之间
- ??运算符为空结合运算符,是一个二元运算符,允许给可能等于NULL的表达式提供另一个值,op1??op2等价于op1==null?op2:op1;
List
myCollection=new List (); - Item 获取或设置指定索引处的元素。
- Count 获取 List 中实际包含的元素数。
- Add 将对象添加到 List 的结尾处。
- AddRange 将指定集合的元素添加到 List 的末尾。
- Clear 从 List 中移除所有元素。
- Contains 确定某元素是否在 List 中。
- ConvertAll 将当前 List 中的元素转换为另一种类型,并返回包含转换后的元素的列表。
- Equals(Object) 确定指定的 Object 是否等于当前的 Object。 (继承自 Object。)
- Remove 从 List 中移除特定对象的第一个匹配项。
- Sort() 使用默认比较器对整个 List 中的元素进行排序。
- ToArray 将 List 的元素复制到新数组中。
- ToString 返回表示当前对象的字符串。 (继承自 Object。)
using System.Collections.Generic; List
animalCollection = new List (); animalCollection.Add(new Cow("Jack")); animalCollection.Add(new Chicken("Vera")); foreach (Animal myAnimal in animalCollection) { myAnimal.Feed(); } 或者 public class Animals:List {} - 对泛型进行排序
- System.Collections.Generic名称空间包含
List
T类型对象集合,Dictionary
与K类型的键值相关的V类型的项的集合 List
- 使用泛型接口
IComparer
和IComparable int IComparable
.CompareTo(T otherObj) bool IComparable
.Equals(T otherObj) int IComparer
.Compare(T objectA,T objectB) bool ICompare
.Equals(T objectA,T objectB) int IComparer
.GetHashCode(T objectA)
- 给列表排序,需要有一个方法来比较两个T类型的对象,要在列表中搜索,需要用一个方法来检查T类型的对象
- 两个泛型委托:
Comparison
int method(T objectA,TobjectB) Predicate bool method(T targetObject) public class Vectors : List
{ public Vectors() { } public Vectors(IEnumerable initialItems) { foreach (Vector vector in initialItems) { Add(vector); } } public string Sum() { StringBuilder sb = new StringBuilder();//表示可变字符字符串 Vector currentPoint = new Vector(0.0, 0.0); sb.Append("origin"); foreach (Vector vector in this) { sb.AppendFormat(" + {0}", vector); currentPoint += vector; } sb.AppendFormat(" = {0}", currentPoint); return sb.ToString(); } } public static class VectorDelegates { public static int Compare(Vector x, Vector y) { if (x.R > y.R) { return 1; } else if (x.R < y.R) { return -1; } return 0; } public static bool TopRightQuadrant(Vector target) { if (target.Theta >= 0.0 && target.Theta <= 90.0) { return true; } else { return false; } } } class Program { static void Main(string[] args) { Vectors route = new Vectors(); route.Add(new Vector(2.0, 90.0)); route.Add(new Vector(1.0, 180.0)); route.Add(new Vector(0.5, 45.0)); route.Add(new Vector(2.5, 315.0)); Console.WriteLine(route.Sum()); Comparison sorter = new Comparison (VectorDelegates.Compare);//委托 route.Sort(sorter);//这个个语句可以简化route.Sort(VectorDelegates.Compare); Console.WriteLine(route.Sum()); Predicate searcher = new Predicate (VectorDelegates.TopRightQuadrant);//委托 Vectors topRightQuadrantRoute = new Vectors(route.FindAll(searcher)); Console.WriteLine(topRightQuadrantRoute.Sum()); Console.ReadKey(); } } - 使用泛型接口
Dictionary
该类型建立键/值对应这个类需要实例化两个类型,分别用于键和值
Dictionary
things=new Directionary () things.Add("Green Things",29); foreach(strign key in things.Values) { using key; } foreach(int value in things.Values) { usign value } foreach(KeyValuePair thing in things) { using things.key and things.value } + 每个键都独立的,如果相同抛出异常`Dictionary
things=new Directionary (stringComparer.CurrentCultureIgnoreCase)`自己的类用作键,不区分发小写比较,这样同一个类就会出现异常 - System.Collections.Generic名称空间包含
- 定义泛型
- 要创建泛型类
class MyGenericClass
{ } - 定义泛型后就可以像使用其他数据类型一样使用它们
- default关键字,default(T1),如果T1是值类型使之默认为0,如果为引用就默认为null
- 约束
class MyGenericClass
where T:constraint1,constraint2 { } //约束必须在继承后面 + struct类型必须为值类型 + class类型必须为引用类型 + baseclass类型必须为基类或继承基类 + interface类型必须有接口或实现了接口 + new()类型必须有一个公共的无参构造函数
public abstract class Animal { protected string name; public string Name { get { return name; } set { name = value; } } public Animal() { name = "The animal with no name"; } public Animal(string newName) { name = newName; } public void Feed() { Console.WriteLine("{0} has been fed.", name); } public abstract void MakeANoise(); } public class Chicken : Animal { public void LayEgg() { Console.WriteLine("{0} has laid an egg.", name); } public Chicken(string newName) : base(newName) { } public override void MakeANoise() { Console.WriteLine("{0} says 'cluck!';", name); } } public class Chicken : Animal { public void LayEgg() { Console.WriteLine("{0} has laid an egg.", name); } public Chicken(string newName) : base(newName) { } public override void MakeANoise() { Console.WriteLine("{0} says 'cluck!';", name); } } public class Cow : Animal { public void Milk() { Console.WriteLine("{0} has been milked.", name); } public Cow(string newName) : base(newName) { } public override void MakeANoise() { Console.WriteLine("{0} says 'moo!'", name); } } public class SuperCow : Cow { public void Fly() { Console.WriteLine("{0} is flying!", name); } public SuperCow(string newName): base(newName) { } public override void MakeANoise() { Console.WriteLine( "{0} says 'here I come to save the day!'", name); } } public class Farm
: IEnumerable where T : Animal { private List animals = new List (); public List Animals { get { return animals; } } public IEnumerator GetEnumerator()//实现IEnumerator 接口 { return animals.GetEnumerator();//才能实现foreach } IEnumerator IEnumerable.GetEnumerator()//IEnumerator 接口继承IEnumerable,必须实现IEnumerable接口 { return animals.GetEnumerator(); } public void MakeNoises() { foreach (T animal in animals) { animal.MakeANoise(); } } public void FeedTheAnimals() { foreach (T animal in animals) { animal.Feed(); } } public Farm GetCows() { Farm cowFarm = new Farm (); foreach (T animal in animals) { if (animal is Cow) { cowFarm.Animals.Add(animal as Cow); } } return cowFarm; } // For generic methods section. //public Farm GetSpecies() where U : T //{ // Farm speciesFarm = new Farm(); // foreach (T animal in animals) // { // if (animal is U) // { // speciesFarm.Animals.Add(animal as U); // } // } // return speciesFarm; //} } class Program { static void Main(string[] args) { Farm farm = new Farm (); farm.Animals.Add(new Cow("Jack")); farm.Animals.Add(new Chicken("Vera")); farm.Animals.Add(new Chicken("Sally")); farm.Animals.Add(new SuperCow("Kevin")); farm.MakeNoises(); Farm dairyFarm = farm.GetCows(); dairyFarm.FeedTheAnimals(); foreach (Cow cow in dairyFarm) { if (cow is SuperCow) { (cow as SuperCow).Fly(); } } Console.ReadKey(); } } Jack says 'moo!' Vera says 'cluck!'; Sally says 'cluck!'; Kevin says 'here I come to save the day!' Jack has been fed. Kevin has been fed. Kevin is flying! - 泛型运算符
public static implicit operator List(Farm farm)
{
Listresult=new List();
foreach(T animal in farm)
{
result.Add(animal);
}
return result;
}
pulic static Farm operator +(Farm farm1,List farm2)
{
Farm result= new Farm ();
foreach(T animal in farm1)
{
result.Animals.add(animal);
}
foreach(T animal in farm2)
{
if(!result.Animals.Contains(animal))
{
result.Animals.add(animal);
}
}
return result;
}
public static Farm operator +(List farm1,Farm farm2)
{
return farm1+farm2;
}
Farm newFarm=farm + dairyFarm;
- 泛型结构
结构是值类型,不是引用类型
可以用泛型来构造泛型结构
public struct Mystructs
{
public T1 item1;
public T2 item2;
}
- 泛型接口
interface MyFarmingInterface where T:Animal
{
bool AttempToBreed(T animal1,T animal2)
T oldestInHerd{get;}
}
- 泛型方法
public T GetDefault()
{
return default;//使用关键字default,为类型T返回默认值
}
int myDefaultInt=GetDefault();//调用
public class Defaulter
{
public T GetDefault()
{
return default;//通过非泛型类实现泛型
}
}
public class Defaulter
{
public T2 GetDefault()where T2:T1
{
return default;//通过泛型类与泛型方法不同的标识
}
}
public farm getspecies() where u : t
{
farm speciesfarm = new farm();
foreach (t animal in animals)
{
if (animal is u)
{
speciesfarm.animals.add(animal as u);
}
}
return speciesfarm;
}
- 泛型委托
public delegate T1 MyDelegate(T2 op1,T2 op2) where T2:T1;
- 变体:是抗变和协变的统称
需要定义编译过程
Cow myCow=new Cow("gemeroter");
Animal myAnimal=myCow;//在类中这样是可行的
IMethaneProduce cowMethaneProduce=myCow;
IMethaneProduce animalMethaneProduce=cowMethaneProduce;//在接口中这样是不可以的
//在泛型中所有类型都是不变的,但是可以在泛型接口和泛型委托的基础上定义变体类型参数
//IMethaneProduce接口类型参数必须是协变的,
//抗变与协变类似但是方向相反,抗变可以把接口放在派生类型中
IGrassMuncher cowGrassMuncher=myCow;
IGrassMuncher supercowGrassMuncher=cowGrassMuncher;
+ 协便
//协变需要使用关键字out
public interface IMethaneProduce//这样IEnumerable对象可以放在IEnumerable类型的变量中
{
...
}
static void Main(string[] args)
{
List cows=new List();
cows.Add(new Cow("dfdf"));
cows.Add(new SuperCow("ddddd"));
ListAnimals(cows);
}
static void ListAnimals(IEnumerable animals)
{
foreach(Animal animal in animals)
{
Console.WriteLine(animal.Tostring());
}
}
+ 抗变
//抗变需要使用关键字in
public interface IGrassMuncher//抗变类型只能用作方法参数
{
...
}
public class AnimalNameLengthComparer:IComparer
{
public int Compare(Animal x,Animal y)
{
return x.Name.Length.CompareTo(y.Name.Length)
}
}
事件
- 事件必须订阅,可以由多个订阅
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 (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
MessageArrived;//事件,EventHandler 为委托模板,将消息类传入 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
- 匿名方法,纯粹是为了用作委托目的而创建的
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来实现
补充
- 对象初始化器
- 省略构造函数的括号,自动调用无参数构造函数,之后调用初始化器
- 同时对象初始化器还可以进行嵌套
//初始化器,使用默认构造函数实现赋值
=new { = , = , = , ... } 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 };
+ 使用var关键字,因为匿名类型没有可以使用的标识符, + 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 GetWords(
string sentence,
bool capitalizeWords = false,
bool reverseOrder = false,
bool reverseWords = false)
{
List words = new List(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);
- 扩展方法
- 要创建和使用扩展方法必须:
- 创建一个非泛型静态类
- 使用扩展方法的语法,为所创建的类添加扩展方法,做为静态方法
- 确保使用扩展方法的代码用using语法导入包含扩展方法类的名称空间
- 通过扩展类型的一个实例调用扩展方法,与调用扩展类型的其他方法一样。
- 扩展方法的要求:
- 方法必须是静态的
- 方法必须包含一个参数,表示调用扩展方法的类型实例
- 实例参数必须是方法定义的第一个参数,
- 除了this关键字外,实例参数不能有其他修饰符
public static class ExtensionClass { public static
(this instance , ) { ... } } public static class ExtensionClass { public static (this instance , ) { ... } } myVar; //myVar is initialized by code not shown here myVar. (); myVar; ExtensionClass. (myVar); //导入后可以通过IntelliSense查看扩展方法 //定义了一个扩展方法后还可以将其运用到派生于这个类型的子类型中 namespace ExtensionLib { public static class WordProcessor { public static List GetWords( this string sentence, bool capitalizeWords = false, bool reverseOrder = false, bool reverseWords = false) { List words = new List (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表达式
- 定义一个事件处理方法,其返回类型和参数匹配要订阅的事件需要委托返回类型和参数
- 声明一个委托类型的变量
- 把委托变量初始化为委托类型的实例,实例指向事件处理方法
- 把委托变量添加到时间的订阅者列表中
正常的事件 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
( this Enumerable source, Func func); public static TAccumlate Aggregate ( this Enumerable source, TAccumlate seed, Func func); public static TResult Aggregate (TResult>(this IEnumerable source, TAccumlate seed, Func func, Func resultSelectoe); //这是一个累计器 int[] myIntArray={1,2,3,4}; int result = myIntArray.Aggregate(...); int result = myIntArray.Aggregate (...); 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
( 0, (a, b) => a + b.Length)); Console.WriteLine(curries.Aggregate ( "Some curries:", (a, b) => a + " " + b, a => a)); Console.WriteLine(curries.Aggregate ( "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)); } }