我说会C#,实际都是皮毛。买了《C#入门经典》(v6)和《C#高级编程》(v9),决定系统地学一遍。这是我的读书笔记而已,不是什么教程,如果有错误,还请各路大神指正。
希望自己坚持看完这两本书,做好笔记,补缺补漏。
Framework 框架
Compact 精简的
OOP(Object-Oriented Programming,面向对象编程)
CTS(Common Type System,通用类型系统)指如int等最基础的数据类型
CLR(Common Language Runtime,公共语言运行库)负责管理用.Net库开发的所有程序的执行
CIL(Common Intermediate Language,通用中间语言)
JIT(Just-In-Time,准时、实时、即时)
GAC(Global Assembly Cache,全局程序集高速缓存)
WPF(Windows Presentation Foundation,Windows呈现基础,取代原来的Windows Form)
WCF(Windows Communication Foundation,Windows通讯平台)
ASP.NET Active Server Pages.NET
ADO.NET Active Data Object.NET
IDE(Integrated Development Environment,集成开发环境)
概念
托管代码 类型安全
Console Application 控制台应用程序
unsigned 无符号
#region 代码块(可折叠)
#endregion
@逐字指定字符,除双引号外。
基础类型中,string是引用类型,可指定null,其他类型为值类型。
数学运算符 - 取反,+ 不变。
int i = -3;
k = +i;//k=-3
k = -i;//k=3
++运算符放在操作数前面时,操作数在任何其他表达式计算前受到影响,而++运算符放在操作数后面时,操作数在完成表达式计算后才受到++运算符影响。例子见P36。
int i = 3, j = 3,k=0;
k = i++;//k=3
k = ++j;//k=4
为命名空间取别名。
using SL = System.Linq;
逻辑运算符 && || 与 & | 功能一样,但推荐使用前者(性能较高)。
int i = 0;
bool a = (i != 0) && (2 / i > 0);//i为0时,不计算(2 / i > 0)
bool b = (i != 0) & (2 / i > 0);//i为0时,仍计算(2 / i > 0),引发除0错误
goto语句有用但最好不用。
int i = 5;
goto lable2;
i++;//未执行
lable2:
i--;//i为4
IntelliSense 智能感知
JaggedArray 锯齿数组
char存储的是数值,所以char被当做个数值类型。
checked/unchecked 用于类型转换时,表达式的溢出检查。(不检查时,出现溢出则数据丢失)。
int i = 10000;
byte k = checked((byte)i);
enum 定义枚举,详见P81。
enum Day:short//默认基础类型是int
{
Monday=1,//默认从0开始
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
数组定义,详见P88。
//一维数组
const int constSize = 5;
int[] array1 = { 1, 2, 3, 4, 5 };//合法,数组默认值{ 1, 2, 3, 4, 5 }
int[] array2 = new int[constSize];//合法,数组默认值为int的默认值0
int[] array3 = new int[constSize] { 1, 2, 3, 4, 5 };//合法,数组默认值{ 1, 2, 3, 4, 5 }
int size = 5;
int[] array4 = new int[size];//合法,数组默认值为int的默认值0
int[] array5 = new int[size] { 1, 2, 3, 4, 5 };//【非法】,此处size应使用常量值(因为已初始化元素)
//矩形数组(每行的元素个数相同)
int[,] array6 = new int[3, 4];//二维数组,3行4列
int[, , ,] array7 = new int[1, 2, 3, 4];//多维数组
//锯齿数组(数组的数组,每行的元素个数可能不同)
int[][] array8 = new int[][]
{
new int[]{1},
new int[]{1,2},
new int[]{1,2,3}
};
foreach循环与for循环的主要区别:foreach循环只可只读访问数组元素,而不能修改。
以下标方式访问string的字符时,可读不可写。
string s = "abc";
char c = s[1];//合法,c='b'
s[1] = 'j';//非法,s[1]只读
string.PadLeft() 用于字符串对齐。
string s = "abc",a,b;
a = s.PadLeft(10);//在s左部填充空格以达到长度10
b = s.PadLeft(10,'-');//在s左部填充'-'以达到长度10
parameter 形式参数(函数定义的一部分)
argument 实际参数(调用代码传递给函数的参数)
参数数组,用params关键字为函数指定一个(只能一个)特殊参数,该参数只能是函数定义的最后一个参数。
static int Sum(params int[] nums)//params参数数组必须是参数定义中的最后一个参数
{
int sum = 0;
foreach (int n in nums)
{
sum += n;
}
return sum;
}
//使用
int sum=Sum(1,2,3,4);//可以指定任意多个int参数
ref参数:用作ref参数(引用参数)的变量有两个限制:已初始化过的非const变量。
out参数:用作out参数(输出参数)的变量可以是未赋值的变量。在函数使用out参数时,应将其看做是尚未赋值的变量。详见P113。
函数签名:函数名+参数。(唯一标志)
函数重载:函数名相同,但函数签名不同。
Main()函数与命令行参数的使用。详见P120。
delegate委托。委托是定义一个函数的类型(指定参数和返回类型),之后可将匹配该类型的函数作为该类型的变量来使用。详见124。
delegate double MyDelegate(int a, int b);//声明委托,指定参数与返回类型
double Multiply(int x, int y) { return x * y; }//与委托匹配的函数1
double Divide(int x, int y) { return x / y; }//与委托匹配的函数2
//使用
MyDelegate d;
double result;
d = new MyDelegate(Multiply);//实例化委托,此时d实际上就是函数Multiply
result = d(1, 2);// 1*2
d = new MyDelegate(Divide);//实例化委托,此时d实际上就是函数Divide
result = d(1, 2);// 1/2
Toggle BreakPoint 插入断点 (Toggle 开关)
SEH(Structured Exception Handling,结构化异常处理)
非中断模式调试:诊断输出(C#功能,Debug.WriteLine()+Trace.WriteLine())+ 跟踪点(VS功能)
中断模式调试:配置断点(断点可以配置几种中断条件)+ Debug.Assert()
Debug.WriteLine() 只能用于调试模式(Debug),将文本写到Output输出窗口。
Trace.WriteLine() 可用于调试+发布模式(Release),将文本写到Output输出窗口。
System.Diagnostics.Debug.WriteLine("a debug message");//仅在调试模式起作用
System.Diagnostics.Trace.WriteLine("a trace message");//在调试+发布模式均起作用
UML(Unified Modeling Language,统一建模语言)
IDisposable接口与using关键字释放资源。P162。
基类(父类)派生出派生类(子类),派生类继承基类。
seal 密封类,不能用作基类,所以没有派生类。
基类的成员可虚拟(virtual),由派生类重写(override)实现。
abstract 抽象类,不能直接实例化,需被继承。抽象类有抽象成员(需被重写)和非抽象成员(有具体实现)。
interface接口,用于定义通用功能,不可自己实现。
abstract class(抽象类)与interface(接口):
在语法、用法上十分相近,但本质上不同:
抽象类:共性对象(强调属性,是对有相似属性的对象的概括,主要用作对象系列的基类)
接 口:相同功能(强调功能,完全不同的类也可以具有相同的功能)
virtual与abstract:详见P193。
均用于基类中指定成员可被派生类用override关键字重写,区别是:
virtual修饰的方法必须有方法体(实现),abstract修饰的方法不能有方法体(声明);
virtual可以被派生类重写或不重写,abstract必须被重写;
只有抽象类才可使用abstract修饰方法;
finalize 完成;结束
DLL(Dynamic Link Library,动态链接库,又称“应用程序拓展”)
class类、interface接口;几种修饰关键字:internal (程序集内部访问)/ public、abstract / sealed
类型比较object.GetType() == typeof(Object)。P181。
无参数构造函数为默认构造函数。
定义的类如果没有使用基类,则只继承于基类System.Object(C#中别名object)。实例化派生类时,基类调用其默认构造函数。因此,实例化一个类时,总是首先调用System.Object.Object()。
值类型与引用类型:值类型(简单类型+结构体struct),引用类型(string+类class),详见P195。
关于对象复制,详见P197+P254。
shallow copy 浅度复制:System.Object的MemberwiseClone(),对引用类型对象复制的是引用。
deep copy 深度复制:实现ICloneable接口的Clone()方法,详见P254。
refactoring 重构
implementation 实现
readonly关键字:表明字段只能由构造函数或初始化赋值语句进行赋值。
const成员也是静态的,所以不需要static修饰。
字段与属性的区别:
//私有字段,camelCasing
private int number;//字段
//公共字段,PascalCasing
public string Name;//字段
public int Number//属性
{
get { return number; }
protected set //可为访问器设置访问性
{
//属性控制
if (value > 0)
{
number = value;
}
else
{
//抛出异常提供控制权
throw new ArgumentOutOfRangeException("Number", value, "Number 必须大于0");
}
}
}
自动属性:由编译器声明对应私有字段
public int Number { get; protected set; }
隐藏基类方法:派生类继承方法前加关键字new。
接口的实现。P217。(隐式、显式实现区别,特殊属性访问器)
capacity 容量
indexer 索引符
boxing 封箱/装箱
unboxing 拆箱
operator overloading 运算符重载
implicit / explicit 隐式/显式
CollectionBase、DictionaryBase:自定义集合类可继承这两个类,详见P242。
yield关键字:在迭代器块中选择在foreach循环中使用的值。
封箱/装箱:封箱可以看作是浅度复制的过程,值类型封箱引用新副本,引用类型封箱引用源引用类型。
is运算符:用于检查对象是否可以转换为指定类型(基类或接口),可以转换运算符返回true。
classa.GetType()== typeof(ClassA):用于检查对象是否就是指定类型(而不是其派生类等)。
as运算符:用于把一种类型转换为指定的引用类型。以下两句代码等价,但有不同:as运算符进行类型转换失败时返回null,而不抛出异常。详见P279。
return cards.Clone() as Cards;
return (Cards)cards.Clone();
运算符重载:P264
//运算符重载
public static bool operator ==(Card c1, Card c2)
{
return (c1.suit == c2.suit) && (c1.rank == c2.rank);
}
public static bool operator !=(Card c1, Card c2)
{
return !(c1==c2);//调用其他运算符减少代码量和错误可能
}
//重写该方法可以确保任何比较技术得到的结果相同
public override bool Equals(object obj)
{
return this == (Card)obj;//具体比较操作
}
//重写该方法可以确保任何比较技术得到的结果相同
public override int GetHashCode()
{
return 13 * (int)suit + (int)rank;//根据状态,返回对象实例的唯一int值
}
IComparable接口:在要比较对象的类中实现CompareTo(),比较对象与对象,如:a.CompareTo(b)
IComparer接口:在单独的比较类(比较器)中实现Compare(),如:comparer.Compare(a,b)
arrayList.Sort():使用项的CompareTo()进行默认排序,或传入一个比较器(实现IComparer接口的类)实例进行自定义排序,如arrayList.Sort(comparer)。详见P274。
implicit / explicit 关键字:用于指定隐式/显式类型转换,详见P279。
public static explicit operator ClassA(Class2 obj)
{
ClassA returnObj = new ClassA();
checked { returnObj.val = obj.val; }//在显式转换中建议使用checked检查溢出
return returnObj;
}
generic 通用的;一般的
N/A(Not Applicable,不适用(不是Not Available))
predicate 断言;判定
System.Nullable:可空类型,用于使值类型为空null。
//以下两句代码等价
System.Nullable<int> nullableInt1 = null;
int? nullableInt2 = null;
??运算符:可用于为可空类型提供默认值,不需要使用if判断。
op1 ?? op2 //等价于下句
op1 != null ? op1 : op2 //当op1非空时返回op,1,否则返回op2
泛型元素:类、方法、接口、委托,如:
List<T>、Dictionary//泛型类
IComparer<T>、IComparable<T>//泛型接口
Comparison<T>、Predicate<T>//泛型委托
...//泛型方法
default关键字:定义泛型类中根据T是值类型/引用类型赋予值类型默认值/null。
where关键字:约束可用于实例化泛型类的类型。
协变关键字out:协变类型参数只能用作返回值或者属性get访问器(输出)。
抗变关键字in:抗变类型参数只能用作方法参数,不能用作返回类型(输入)。
alias 别名
nest 嵌套;巢
subscribe 订阅
:: 运算符:当命名空间的别名与实际存在的命名空间重名时,编译器默认使用实际命名空间,此时可使用::符号迫使编译器使用由using语句定义的别名。
global关键字:实际是顶级根名称空间的别名。
定制异常:继承基类Exception,用try{…}catch{…}finally{…}捕获。
event关键字:定义事件
//Step0.定义参数类型(必要时),继承EventArgs
public class MyEventArgs : EventArgs
{
private string message;
public string Message { get; set; }
public MyEventArgs(string msg) { message = msg; }
}
//Step1.定义委托,指定事件处理方法的参数和返回类型
//(标准事件委托:void返回类型+object事件源+继承EventArgs的事件参数)
public delegate void MyEventHandler(object sender, MyEventArgs e);
//Step2.定义事件,并指定要使用的委托类型。
public event MyEventHandler LastCardDrawn;
//触发事件
public Card GetCard(int cardNum)
{
//当事件为null时说明该事件没有订阅者,不会引发事件
if (cardNum == 51 && LastCardDrawn != null)
LastCardDrawn(this, new MyEventArgs("自定义参数"));//触发事件,传递事件参数
return cards[cardNum];
}
事件处理程序最好使用void返回类型;有返回值时(非void),访问的是最后一个订阅事件的处理程序返回值。
EventHandler、EventHandler:.NET定义的委托类型,使用标准的事件处理模式,其原型为:
public delegate void EventHandler(object sender, EventArgs e);
public delegate void EventHandler(object sender, TEventArgs e);
匿名方法:使用delegate关键字就地定义一个纯粹用作委托目的的处理程序,详见P335。
Attribute特性:对代码进行装饰,如[DebuggerStepThrough],通过反射读取特性值,详见P344。
COM(Component Object Model,组件对象模型)
aggregate 聚合、聚集
accumulate 积累
对象初始化器、集合初始化器:合并实例化与初始化的简化方式。
List farm = new List//集合初始化器
{
new Chicken(),//调用构造函数进行初始化
new Cow { Name="Norris" }//对象初始化器:不必显式调用构造函数,直接使用公开属性-值对进行赋值
};
var关键字:进行隐式类型化,依赖于编译器确定变量类型(类型推理)
//用var声明变量时需同时初始化,编译器才能确定变量类型
var myVar = new[] { 1, 2, 3 };//myVar被隐式设置为int[]
匿名类型:只读
var curry = new
{
Name = "name",
Age = 5
};
dynamic关键字:动态类型,主要用于处理其他语言创建的对象,尽量避免使用。详见P362。
可选参数:为参数提供默认值,使其成为可选参数。避免使用。(可省去部分函数重载代码)
Optional特性也可定义可选参数,详见P366。
public List<char> GetChar(
string str,
bool capitalized = false //可选参数,必须位于参数列表末尾
)
{
List<char> chars = new List<char>();
string newStr = capitalized ? str.ToUpper() : str;
foreach (char c in newStr)
chars.Add(c);
return chars;
}
//使用
List<char> chars = GetChar("string");//等价于
List<char> chars = GetChar("string",false);
命名参数:当一个方法存在多个可选参数时,可使用<形参名>:<形参值>的形式使用指定参数。
List<char> chars = GetChar("string",capitalized : false);
扩展方法:给现有类型添加扩展功能,可提供实用方法库,详见P373。
Lambda表达式(用作委托):只用单一表达式即可完成任务时最佳,否则定义非匿名方法较好
(int a, int b) => a + b//编译器会提取Lambda表达式,创建一个匿名方法
(a, b) => a + b//隐式类型化,由编译器联系上下文确定参数类型,更加灵活
(a, b) => { b++; return a + b; }//含多条代码且返回类型不是void时,需用{}包含代码,用return指定返回值
a => a + b//只有1个隐式类型化参数,可省略括号()
() => Math.PI//没有参数,用空号()表示
Lambda表达式表示为泛型类型:
泛型委托 | 类型参数 | 返回类型 | 举例 |
---|---|---|---|
Action | 0 | void | 泛型委托Action<> 可表示表达式() => Console.WriteLine("HelloWorld") |
Action<> | <=8 | void | 泛型委托Action 可表示表达式(a, b) => Console.WriteLine((a + b).ToString()) |
Func<> | <=9 | 类型参数中的最后一个 | 泛型委托Func 可表示表达式(a, b) => a.Length + b.Length (返回值为int) |
Lambda表达式和集合,详见P381。
Lambda表达式结合System.Linq的使用见第23章LINQ简介。
(第一部分 C#语言 完)