目录
1.1 官方编程文档
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC#\Specifications\2052\CSharp Language Specification.doc
1.2 类与结构
属性 | 描述对象的特征,也可对字段进行封装(get,set) |
字段 | 类或结构体内部定义的变量 |
方法 | 类的某种动作或行为 |
构造器 | 构造函数,在创建实例对象时调用,来进行类的初始化工作 |
事件 | 特定条件下触发的行为 |
结构只能定义带参构造函数,不能继承和派生,但可以实现接口
结构中不能实例字段初始值
1.3 引用类型与值类型
类是引用类型
class Program
{
static void Main(string[] args)
{
person p1 = new person();
person p2 = p1;
p1.Name = "Jack";
p1.Age = 30;
Console.WriteLine(p2.Name + p2.Age.ToString());
p1.Name = "Bob";
p1.Age = 27;
Console.WriteLine(p2.Name + p2.Age.ToString());
}
}
class person
{
public string Name { set; get;}
public int Age { set; get; }
}
结构是值类型
class Program
{
static void Main(string[] args)
{
person p1 = new person();
person p2 = p1;
p1.Name = "Jack";
p1.Age = 30;
Console.WriteLine(p2.Name + p2.Age.ToString());
p1.Name = "Bob";
p1.Age = 27;
Console.WriteLine(p2.Name + p2.Age.ToString());
}
}
struct person
{
public string Name { set; get;}
public int Age { set; get; }
}
引用对象与值对象的总结
1.4 ref参数与out参数
static void getPerson1(person p)
{
p.Name = "Amiee";
p.Age = 25;
}
//此版本对参数p的操作会反应到实参中
static void getPerson2(person p)
{
p = new person("Ray", 27);
}
//此版本在传参数时,参数p指向的实参对象,但是进入函数后,参数p的指向发生了变化,对p参数的操作便不会再反映到实参中
上述两个函数的阐述传递过程如下:
为解决上述问题,引入ref参数和out参数:
ref参数传入前必须初始化,out传入前不用初始化
static void getPerson3(ref person p)
{
p = new person("Ray", 28);
}
static void getPerson4(out person p)
{
p = new person("Ray", 29);
}
均完成了对实参的修改
1.5 方法重载
具有不同返回值的同名方法可重载
具有不同参数个数、参数类型和参数顺序的同名方法
有无ref或out修饰符修饰的同名方法
1.6 静态类与静态成员
静态类的所有成员都必须的是静态的
类的静态成员可用类名直接调用
1.7 继承与多态
(1)可访问性
对internal可访问性的验证见博文https://blog.youkuaiyun.com/m1m2m3mmm/article/details/95667772
(2)继承
- 子类构造时,线调用父类的构造函数再调用自己的构造函数;子类对象被销毁时,先调用自己的析构函数再调用父类的析构函数
- 在继承时,子类的访问属性不高于父类的访问属性,比如基类是internal则不允许子类被定义为public
(3)覆盖与多态
- 子类会自动覆盖基类中定义的非virtual关键字修饰的同名方法,但此时直接定义子类对象,调用的是子类的同名方法;若定义指向子类的父类对象,调用的是父类的同名方法;
- 使用virtual关键字修饰的方法,在被指向子类的基类对象调用时,实际上调用的是子类对象的方法;直接定义子类对象时,调用的是子类自己的同名方法
(4)定义不可被继承的类
sealed class ClassName
{
};
1.9 抽象类与接口
抽象类可以定义非抽象成员,这些成员被其派生类继承;抽象类的不能被实例化
非抽象类不能定义抽象成员
接口与抽象类的区别:【参考之前博文类与对象学习小结】
- 接口是一种类型,不提供任何实现的代码,没有构造函数,接口定义的方法是不带任何访问关键字的
- 抽象类的是类的一种,可以定义非抽象成员和构造函数,成员具有访问属性
- 不允许一个类同时提供对多个基类 的实现;但允许一个接口同时实现多个接口
- 实现接口时,必须对接口提供对接口所有成员的实现
接口的显式实现
在实际的开发过程中,如果多个接口定义命名的成员,而这些接口又同时被同一个类实现,此时为了避免成员之间的冲突,需提供对接口的显式实现,在方法调用时,只能通过定义接口进行调用;通过实现类对象无法进行调用,除非将其强制转化为对应的接口。
1.10 拓展方法
在不继承的前提下,对现有类型进行拓展;拓展方法定义在静态类中的静态方法
//拓展方法的定义
public static class stringExt//静态类,静态成员
{
//将字符串转化为空格分开的字符串
public static string SplitSpace(this string str)
{
return string.Join(" ", str.ToCharArray());
}
//将数值转化为其绝对值
public static int abs(this int num)
{
return num >= 0 ? num : 0 - num;
}
}
调用拓展方法
string str = "Ray";
Console.WriteLine(str.SplitSpace()); //并不会对str的值产生任何影响,只是产生了一个新的空格分割字符串
int num = -10;
Console.WriteLine(num.abs().ToString());
1.11 委托与事件
- 类似于C语言中的函数指针,C#也可以将函数当作方法传给其他方法
- 定义委托与定义其他方法类似,不过需要使用关键字delagate关键字
- 委托类型的隐含公共基类:System.Delagate和System.MulticastDelegate(多播委托);多播委托被调用,会调用与之关联的所有方法;
- 当将委托当作参数传递某个方法时,是值类型的传递;在方法内部对引用的委托实例进行变更时,并不会反应到原来的委托实例中
- 可以总结出委托使用的三个关键点:定义委托,委托实例化,委托调用
事件是委托类型,要声明事件就需要定义事件封装类型的委托;然后在类中使用event关键字,同时为了让派生类可以重写该事件,常将其定义为protected类型。习惯上,事件的命名方式为On<事件名>
以学生听到铃声就知道是下课为例,说明事件与委托的关系:铃声响,代表事件发生;学生知道下课就是对事件的响应
标准事件与委托
更为详细的分析见博文委托与事件
1.12 枚举类型
使用枚举类型的好处:
避免意外的调用,调用者只能在给定的值当中做出选择;
可读性强,调用可以方便的识别各枚举值代表的含义
枚举类型继承自Enum类,该类是ValueType类型的派生类,可知枚举类型属于值类型
enum week
{
mondey,friday,sonday
}
枚举类型的基础类型为int,默认从0开始,后值在前值基础上+1
var myWeek = Enum.GetValues(typeof(week));//获取枚举类型中的所有值到一个对应类型的数组中
string name = Enum.GetName(typeof(week), 2);//sonday
string names = Enum.GetNames(typeof(week));//所有项
1.13 数组
声明与赋值
//先声明后赋值
type[] arr = new type[count];
arr[0] = value1;
arr[1] = value2;
//声明同时赋值
type[] arr = new type[] {valueList};
type[] arr = {valueType};
//二维数组
type[,] arr = new type[rowCount, colCount];
arr[0,0] = value00;
arr[0,1] = value01;
数组遍历
for(int i = 0; i < arr.Count; i++)
{
Console.Writeline(arr[i].ToString());
}
//所有数组继承自IEnumerable,可以使用foreach遍历
foreach(type var in arr)
{
Console.Writeline(var.ToString());
}
方法 | 描述 | 返回值 |
Array.Reverse(arr); | 字符串反转 | T[] |
Array.Resize<type>(arr, int newSize); | 改变数组大小;哈希值会发生变化 | T |
Array.Find(arr, new Predicate<T>(dealFun)); | 从头查找满足某规则的值 | T |
Array.Findlast(arr, new Predicate<T>(dealFun)); | 从尾部查找满足某规则的值 | T |
Array.FindAll(arr, new Predicate<T>(dealFun)); | 查找所有满足某规则的值 | T[] |

上述第二组方法需要用到Predicate委托,原型如下:
T为参数类型,obj为数组中待查找元素应满足条件的方法名
private static bool findProc(T obj)
{
if(条件一)
{
//处理代码1
return ture;
}
if(条件二)
{
//处理代码2
return false;
}
}