- 1. C#简介:
1.1 Common type system,CTS,通用类型系统。定义一些基本类型以便在各语言之间交互操作。
1.2 Common language runtime,CLR,公共语言运行库,负责管理用.net 库开发的所有应用程序的执行。
1.3 CIL, common intermediate language,(也叫MSIL)通用中间语言。由VS完成。然后再用JIT(just in time)编译器创建所需的本机代码。
1.4 程序集(exe,dll)除了包含CIL还包含元数据和可选资源,允许程序集是完全自描述的。
1.5 GAC,global assembly cache,全局程序集缓存。用来存放一些可重用的代码
1.6 托管代码,CLR 管理应用程序的运行,方式是管理内存,处理安全性,允许跨语言调试。
1.7 垃圾回收会定期检查内存,删除不用的内容。
1.8 C#是一种类型安全的语言,类型之间转换要遵守严格的规则
- 2. 编写C#程序:
- 3. 变量和表达式:
3.1 注释,/* // #region,#endregion
3.2 简单类型:sbyte,byte,short(2),ushort,int(4),
uint,long(8),ulong,float,double,
decimal,char,bool,string
3.3 变量命名:PascalCase,camelCase
3.4 转义序列:\0,\a,\b,\f,\n,\r,\t,\v
- 4. 流程转换:
1.1 Var1=var2 var3;如果var2或var3中有且仅有一个是true,var1的值就是true,否则false。
1.2 用&&和||比用&,|性能要高,因为后者都计算。
1.3 &只有两个都为1才是1;|只有都为0才是0;~,
1.4 Var1=var2>>var3把var2的二进制值右移var3位,得到var1
1.5 Goto <labelName>
1.6 Break中断switch语句,return中断函数执行,goto想去哪个case就哪个
1.7 把多条case放在一起,一次检查,符合任何一个都执行代码
1.8 Do{}while(xxx);xxx为false时就退出循环
1.9 循环中断:break---立即终止循环;continue---立即终止当前循环(执行下一循环);goto---可以跳出循环到标记好的位置上(不可从外部进入循环);return---跳出循环及其包含的函数
1.10 Test?<test==true>:<test==false>;
- 5. 变量的更多内容:
1.1 彼此之间毫无关系的类型不能强制转换
1.2 Check,uncheck,表达式溢出检查上下文
1.3 Convert转换总要进行溢出检查
1.4 在枚举声明中添加类型 enum <typeName>:<underlyingType>{<value1>,<value2>,<value3>,。。。<valueN>}枚举赋值默认从0开始,也可用=重写
1.5 把string类型转换为枚举值语法:(enumerationtype)Enum.parse(typeof(enumerationtype),enumerationvaluestring);
1.6 结构:struct <typename>{<memberDeclarations>}
1.7 数组声明:<basetype>[] <name>;初始化new <basetype>[num];
1.8 多维数组:<typename>[,]<name>;
1.9 子数组:<typename>[][];
1.10 字符串处理:tochararray();trim();toupper();tolower();spilt();
- 6. 函数:
1.1 一般采用PascalCase形式编写函数名
1.2 C#允许为函数指定一个(仅一个)特定参数,使用params关键字定义参数数组在最后一个参数位置上。可以传递同类型的参数,例如:void test(string s,params int[] i)
1.3 引用参数用ref关键字标明,调用的时候不要忘记ref关键字,这是函数签名的一部分。引用参数在函数外初始化,且是非常量。
1.4 输出参数用 out关键字标明,和ref 差不多,但参数变量必须未赋值
1.5 变量的作用域不是看变量的声明在哪里,而是在哪里初始化
1.6 函数重载就是同样的函数名不一样的函数签名,注意函数的返回类型不是签名的一部分。
1.7 委托~~
- 7. 调试和错误处理:
1.1 输出调试信息的两个函数,debug.writeline() 和 trace.writeline().第一个仅用于调试,第二个可以用于发布程序中。还有一些相关的函数,debug.writelineIf(),trace.writelineIf() 等等
1.2 断点可设置执行宏,条件执行,输出信息
1.3 判定函数:debug.assert(),trace.assert()
1.4 异常处理:如果在catch中在抛出异常,一定要把finally块执行完再到上一层处理抛出的异常
- 8. 面向对象编程简介:
1.1 一切都是对象
1.2 字段一般是私有的,然后通过公有的属性访问
1.3 构造函数用于对象初始化,一般都有默认构造函数,可以把默认构造函数设为私有,再使用非默认构造函数
1.4 析构函数一般是自动运行的,但也可以自己编写代码执行一些重要的操作
1.5 静态成员与对象无关,可在类的实例之间共享,可看作全局对象。可以用来跟踪类被实例化为多少个对象等
1.6 静态构造函数用于对静态成员初始化,一个类只有一个静态构造函数,也只能被调用一次。不能有访问修饰符和参数,只能在两种情况下执行:创建包含静态构造函数的类实例,访问包含静态构造函数的类的静态成员时。这两种情况下会先调用静态构造函数然后再实例化或访问类的静态成员
1.7 接口是类的创建者和使用者之间的契约,每个支持接口X的类都实现了这些方法和属性
1.8 支持Idisposable接口的对象必须实现Dispose()方法,用于释放重要资源。还有一种方法是使用using关键字using(某对象){。。。}这样会在代码快末尾自动调用dispose()方法
1.9 类的继承:抽象类必须被继承,密封类不能被继承
1.10 成员的继承:虚拟成员可由继承他的类重写,派生类中可以提供基类成员的更多实现代码,而不删除原来的代码。抽象成员必须在派生类中提供实现代码
1.11 多态是可以把派生类的对象变量赋给基类变量,然后调用基类方法,属性。接口亦可
1.12 可以通过访问修饰符来完全控制对包含类的操作。因为包含类其实就是用一个成员字段包含对象实例,就实现了包含关系
1.13 对象数组,对象集合
1.14 值类型在内存中某个地方存储自己的内容,引用类型存储指向内存中其他位置的引用。值类型一定要有值(可空类型例外),引用类型可以为null。
- 9. 定义类:
1.1 派生类的可访问性不能高于基类
1.2 接口默认定义为internal,不能被实例化,必须被继承。
1.3 System.object的方法:object(),~object(),equals(object),equals(object,object),referenceEquals(object,object),tostring(),memberwiseclone(),gettype(),gethashcode()
1.4 类的析构函数由带有~前缀的类名执行,之后隐式调用基类的析构函数直到object类的finalize()
1.5 要实例化一个派生类必须先实例化它的基类,所以无论使用什么构造函数实例化一个类总是要先调用system.object.object
1.6 Base关键字指定.net实例化过程中使用基类中有指定参数的构造函数,定制构造函数链
1.7 This关键字指定在调用指定的构造函数前.net实例化过程对当前类使用非默认的构造函数
1.8 接口和抽象类都不能实例化,它们的成员都可被继承。
1.9 接口成员是公共的,抽象类无限制;接口不能包含字段,构造函数,析构函数,静态成员或常量
1.10 抽象类主要用于对象系列的基类,共享某些主要特性,例如共同的目的和结构。接口主要用于类,这些类在基础水平上有所不同,但仍可以完成某些相同的任务
1.11 结构是值类型,其变量不是包含结构的指针,而是包含结构本身。
- 10. 定义类成员:
1.1 Protected和internal关键字可以合并使用,所以有protected internal成员
1.2 公有字段一般用PascalCasing形式命名,私有一般是camelCasing形式
1.3 用了readonly关键字的字段只能在执行构造函数过程中赋值或初始化语句赋值
1.4 静态字段必须通过定义它的类来访问,const定义的常量也是静态的,不需要static关键字
1.5 与公共字段一样,公共方法也用PascalCasing形式命名
1.6 静态方法也只能通过类来访问
1.7 除了访问修饰符还可以关键字:virtual(方法可以重写),abstract(方法必须在非抽象的派生类中重写,这个关键字只用于抽象类中),override(方法重写一个基类方法)使用了override还可以使用sealed关键字阻止进一步重写,extern(方法定义在别处)
1.8 属性的定义和字段差不多,不过可以在访问器上添加访问修饰符来控制访问,但访问器的访问权限不高于属性本身
1.9 属性可以使用virtual,override,abstract关键字,字段不可以
1.10 利用字段重构生成属性和自动属性是非常方便的,但自动属性必须包含get和set访问器,无法创建只读或只写属性
1.11 当从基类继承一个非抽象的成员时也就继承了实现代码,如果继承的成员是虚拟的,就可以用override关键字重写这段代码。无论是否虚拟都可以隐藏这些实现代码,隐藏基类方法最好用new关键字显式标明
1.12 重写的方法是替换了基类中的代码段,即使再通过基类访问也找不回,而隐藏基类方法并不是替换基类中的方法,还可以访问那段代码
1.13 重写或隐藏成员的时候还想访问基类成员,可以用base关键字。但base关键字是基类的对象实例,不能在静态成员中使用
1.14 This关键字最常用功能是把当前类的实例传送给方法,也不能在静态成员中使用
1.15 嵌套类型定义主要用来定义对于其包含类来说是私有的类,这样名称空间中的其他代码就不能访问它。
1.16 接口成员定义的注意点:不允许使用访问修饰符,接口成员不包含代码体,不能定义字段成员,不使用关键字static/virtual/abstract/sealed,类型定义成员是禁止的
1.17 要隐藏基接口的成员可以使用new关键字
1.18 接口对属性的定义类似与类中的自动属性,但未指定应如何存储属性数据
1.19 接口与类一样,可以定义为类的成员,但不能定义为其他接口的成员
1.20 在类中可以用virtual和abstract关键字来实现接口成员,但不能使用static和const
1.21 继承一个实现给定接口的基类,就意味派生类隐式的支持这个接口。如果在派生类中用new隐藏接口的成员,而不是重写它,则通过接口只能访问到基类的实现代码
1.22 由类显式实现接口成员,则该成员只能通过接口来访问,不能通过类来访问
1.23 如果实现带属性的接口,可以补齐访问器,但必须使用更严格的访问修饰符
1.24 用partial关键字定义部分类,把类的定义放在多个文件中,有利于专注某一方面。另外应用于部分类的接口也会应用与整个类
1.25 部分方法不能有返回类型,必须是私有的,参数不能有out类型,也不能使用virtual/abstract/override/new/sealed/extern修饰符。这一切都是为了编译代码时,如果代码包含一个没有实现代码的部分方法可以完全干净的删除该方法和对该方法的引用。说到底,部分方法是为了设计阶段的方便
- 11. 集合,比较和转换:
1.1 IEnumerable接口有唯一方法GetEnumerator,可以迭代集合中的项
1.2 ICollection(继承于IEnumerable)可以获取集合中项的个数,并把项复制到简单的数组类型中
1.3 IList(继承于IEnumerable和ICollection)提供了集合的项列表,允许访问这些项和其他一些功能
1.4 IDictionary(继承于IEnumerable和ICollection)类似于IList,但可通过键值来访问项列表
1.5 ArrayList类提供的一些方法Add(),Remove(),RemoveAt()。AddRange()方法是这个类所特有,可以一次添加好几个项,接受带有ICollection接口的任何对象
1.6 定义集合的时候一般是从CollectionBase类中派生,这个抽象类提供了集合类的很多实现方式(clear(),removeAt(),count()),使用list通过Ilist接口可以访问存储的对象。添加索引符,实现集合访问的强类型化
1.7 使用基类DictionaryBase简化IDictionary接口的实现。使用它可以得到一个继承于DictionaryBase的成员Dictionary,这是一个IDictionary接口,有自己的Add()方法和remove()方法
1.8 基于DictionaryBase的集合类迭代的时候得到的是DictionaryEntry结构,可以通过value和key访问键值与键
1.9 Foreach循环迭代集合类过程:首先调用GetEnumerator()返回一个IEnumerator引用,接着调用IEnumerator接口的MoveNext()方法,如果返回true就使用该接口的Current属性获取对象的一个引用,用于foreach循环。重复直到返回false
1.10 迭代器是一个代码块,按顺序提供要在foreach循环中提供的所有值,一般是一个方法,也可以使用属性访问器等。迭代器的返回类型并不是所枚举的对象类型,而是接口类型IEnumerable(迭代类成员)和IEnumerator(迭代类)。用yield关键字选择要在foreach中使用的值。迭代器返回的项并不是具体的类型,而是object类型,可以被解释成需要的类型
1.11 实现ICloneable接口进行深复制,该接口有一个方法Clone(),这个方法不带参数,返回一个object类型的结果
1.12 Is关键字可以用于对象值比较,可读性强,还可以检查基类(多态)
1.13 封箱是把值类型转换为object类型,或者转换为由值类型实现的接口类型。包含的是原来值类型变量的一个副本的引用。。封箱隐式,拆箱显式。
1.14 封箱的好处其实就是便于存储和传送,毕竟是面向对象
1.15 运算符重载看起来和标准静态方法声明类似,但使用关键字operator和运算符本身,而不是方法名
1.16 IComparable接口在要比较的对象的类中实现,可以比较该对象和另一个对象,而IComparer接口在一个单独的类中实现,可以比较任意两个对象
1.17 IComparable接口提供方法CompareTo(),接受一个对象,返回一个int值
1.18 IComparer接口提供方法Compare(),接受两个对象,返回一个整形结果
1.19 Comparer类提供了IComparer接口的的默认实现方式,可以使用Comparer.Default静态成员获取Comparer类的一个实例,然后接着使用Compare()方法比较两个对象。对于传送的对象要检查它们是否支持IComparable,还允许使用null值表示小于其他对象,如果处理时不要区分大小写就使用CaseInsensitiveComparer类
1.20 As运算符不会抛出异常,如果无法转换就把null赋给变量
- 12. 泛型:
1.1 泛型允许灵活创建类型,这些类型是在实例化时确定,然后在后台动态生成
1.2 可空值类型的声明,Nullable<T>或者T?,除了可以用引用类型的方式来判断是否为空,还可以有属性HasValue,但这个属性却不适用于引用类型,因为必须有对象才能有这个属性,贸然使用会抛出异常
1.3 ??运算符叫空接合运算符,允许给可能等于null的表达式提供另一个值。用来处理可空变量,提供默认值很方便
1.4 System.collections.generic名称空间提供了两个常用的泛型类List<T> 和 Dictionary<K,V>
1.5 很多情况下都不再使用CollectionBase类派生集合类,因为List<T>以同样方式工作,但CollectionBase主要用于向后兼容,使用它的唯一场合是要更多的控制集合类的成员展示
1.6 泛型类可以使用泛型接口IComparer<T>和IComparable<T>,使用方法大同小异,但泛型的比较是强类型的
1.7 Comparison<T>这个委托类型用于排序方法,其返回类型和参数: int method (T objectA,T objectB).
1.8 Predicate<T>:这个委托类型用于搜索方法,返回类型和参数:bool method(T targetObject)
1.9 Dictionary<K,V> 可以迭代集合中的各个项,把每个项作为一个KeyValuePair<K,V>实例来获取。
1.10 定义泛型类的时候和定义类差不多,只不过要用到<>语法,另外default关键字解决了泛型类构造函数中数据初始化类型不确定的问题。Default(T)
1.11 约束类型用where关键字where T1:animal,可以有多个where关键字和多个约束,约束必须出现在继承说明符的后面。如果new()用作约束,它必须是为类型指定的最后一个约束
1.12 泛型类的继承要提供所有必须的类型信息,如果泛型基类有约束,则派生的泛型类也必须在这个约束之下
1.13 其实无论定义泛型类还是泛型接口,方法,委托,结构,只要基础的定义会了就可以,泛型不过是把基础定义里具体的类型当作可变类型,然后提供一个<>存放可变替代符的列表
1.14 泛型的类型参数是不变的,所以为了多态需要变体,协变可用out关键字在类型前标明。对于接口定义,协变类型参数只能用作方法的返回值或属性get访问器
1.15 要把泛型类型参数定义为抗变,可以在类型定义中使用in关键字,对于接口定义,抗变类型参数只能用作方法参数。
- 13. 其他OOP技术:
1.1 Using mynamespaceAlias=myrootnamespace.mynestednamespace
1.2 mynamespaceAlias::myclass 或者 global::system.collections.generic.list<int>
1.3 在system 名称空间中有两个基本的异常类applicationException和systemException,它们派生于exception。systemException用于.NET FrameWork预定义的异常的基类,applicationException由开发人员用于派生自己的异常类,但目前的最佳实践是从exception中派生。
1.4 事件的类型是定义的委托,通过订阅事件来把事件和事件处理程序关联起来
1.5 事件的委托中包含饿了事件处理程序中常见两类参数,object source(引发事件的对象的引用),ElapsedEventArgse(由事件传送的参数)
1.6 用eventHandler和泛型EventHandler<T>类型委托来简化事件,泛型版本允许指定要使用的事件变元的类型
1.7 事件处理程序一般都是void类型返回值,也可以提供返回类型,但系统默认只准访问最后一个订阅该事件的处理程序返回的值
1.8 匿名方法语法:delegate(parameters){};
- 14. C#语言的改进:
1.1 初始化器在实例化对象时,要为每个需初始化的,可公开访问的属性或字段使用名称-值对,语法如下:
<classname> <variablename>=new <classname>
{
<propertyORfield>=value,
…
<propertyORfield>=value
};
1.2 初始化器可以嵌套
1.3 类似数组,集合亦可以合并实例化和初始化
1.4 类型推理需要用到var关键字,变量的类型并不是var,而是根据初始化时候赋给它的值确定。类型推理并不是很有用,但是是其他概念的基础
1.5 使用var关键字初始化匿名类,语法类似于初始化器,但是省略了类名
1.6 动态变量是为了用C#处理另一种语言创建的对象或者C#中未知类型的对象,用dynamic声明,dynamic类型是确实存在的,所以可以只是声明
1.7 动态查找功能由Dynamic Language Runtime(动态语言运行库,DLR)支持,是.NET4的一部分
1.8 Dynamic类型仅在编译期间存在,运行期间会被object类型替代。
1.9 有了动态变量,总是可以访问他的成员,编译期间一定会通过,但是运行期间如果发现不存在这样的成员就会抛异常
1.10 方法定义中可以给方法定义为可选参数,这需要一个默认值,如果没有提供值就使用默认的。如下:
Public List<string>GetWords(string s,bool capa=false){}
默认值必须是字面值,常量值,新对象实例,默认值类型值,大多数情况下提供一个字面值。还要注意,可选参数必须放在后面
1.11 在使用的时候命名参数语法:
Mymethod(
<paramname>:<paramvalue>,
<paramname>:<paramvalue>
)
1.12 使用和定义扩展方法的准备工作:首先要创建一个非泛型的静态类,给所创建的类添加扩展方法作为静态方法,使用扩展方法的时候用using语句把这个类的名称空间导入
1.13 扩展方法定义的注意点:必须是静态的,必须在第一个参数位置包含一个实例参数
1.14 Lambda表达式比匿名方法更简洁,由3个部分组成:放在括号中的参数列表,=>运算符,C#语句(用逗号分隔)
1.15 Lambda表达式中的参数列表要么显示声明类型,要么就全部隐式。只有一个参数时可以省略括号,没有参数就使用空括号
1.16 Lambda表达式如果有返回类型就要代码块中提供return语句,把分隔符后面的语句用{}括起来,里面的语句用;分隔
1.17 Lambda表达式用作委托和表达式树