C#学习笔记
【持续更新】
【2024-07-19】 新建
- 对象初始化语句的执行总是在构造函数执行之后,如:
class Point
{
public int X = 1;
public int Y = 2;
public Point(int x, int y)
{
X = x;
Y = y;
}
}
class Program
{
static void Main(string[] args)
{
Point p = new Point(3,4) {X = 5, Y = 6};
Console.WriteLine($"p.X = {p.X}, p.Y = {p.Y}");
}
}
// X,Y先被实例化为1,2
// 再调用构造函数赋值为3,4
// 最后由初始化语句赋值为5,6
// 所以输出 p.X = 5, p.Y = 6
-
如果坚持使用.NET类,就不需要为类编写析构函数(如果使用了非托管资源,就必须要)
-
readonly修饰符与const修饰符的区别
- 前者在内存中有存储位置,后者没有
- 前者可以是实例字段也可以是静态字段,后面总是静态的
- 前者可以在运行时(仅构造函数)更改,后者不允许更改。
总之,const 更像是c语言的宏定义; readonly 更像是c语言中的const
-
类的属性字段没有存储位置,其行为更像是方法,而不是字段
-
除了特殊类,所有的类都派生自object类,object类是唯一的非派生类
-
new关键字可以用来屏蔽基类的成员及成员函数;
-
派生类若要访问基类隐藏的成员,要使用base关键字。
-
派生类的实例要访问隐藏的基类成员,可以将派生类引用强制转换成基类引用后进行访问。
-
将派生类引用强制转换成基类引用后,访问虚方法仍然访问的是派生类的虚方法!!!!!
-
属性也可以声明为虚属性(virtual/override)
-
创建一个实例的过程,先初始化对象的所有实例成员, 再调用基类的构造函数,再调用自己的构造函数
-
实例成员若没有初值,则会默认初值0(对于引用则为NULL)
-
派生类的构造函数若没有初始化语句,则将调用基类的无参构造
class BaseClass
{
public BaseClass()
{
Console.WriteLine("base construct: default");
}
public BaseClass(string name)
{
Console.WriteLine("base construct: " + name);
}
}
class DerivedClass : BaseClass
{
public DerivedClass(string name)
{
Console.WriteLine("derived construct: " + name);
}
}
class Program
{
static void Main(string[] args)
{
DerivedClass c = new DerivedClass("hello");
Console.ReadLine();
}
}
//输出 :
// base construct: default
// derived construct: hello
- 派生类的构造函数初始化语句可以使用关键字base或者this指明使用哪个构造函数
class BaseClass
{
public BaseClass(int x)
{
Console.WriteLine("base int: {0}", x);
}
}
class DerivedClass : BaseClass
{
public DerivedClass(int x):base(x)
{
Console.WriteLine("derived int: {0}", x);
}
public DerivedClass(float x) : this((int)x)
{
Console.WriteLine("derived float: {0}", x);
}
}
class Program
{
static void Main(string[] args)
{
DerivedClass c = new DerivedClass(1.3f);
Console.ReadLine();
}
}
// 输出:
// base int: 1
// derived int: 1
// derived float: 1.3
-
类的声明默认为internal,而不是public
-
注意访问级别
public class Myclass
{
public int A1; // 所有可访问
private int A2; // 仅类内部可访问
protected int A3; // 仅该类及其继承类可访问
internal int A4; // 仅该程序集可访问
protected internal int A5; // 该类或其继承类或该程序集可访问
}
-
抽象类(abstract) 必须实现(override)所有抽象成员后才能实例化
-
方法、属性、事件、索引器都可以被抽象,而数据成员不可以
-
当需要指明类不能被继承时,使用sealed修饰符标注(密封类)
-
静态类(static标记类)可以有一个静态构造函数(无参)
-
扩展方法、以及声明扩展方法的类必须是静态的、可访问的。
-
标准数字格式说明符
-
值类型与引用类型
尤其注意,结构struct是值类型,class是引用类型 -
无后缀的实属字面常量是double,而不是float!!!
-
引用比较相等时,一般只比较引用是否相等(浅比较),而string与委托是例外,会比较内容是否相等(深比较)
-
implicit与explicit可用于定义类型的隐式转换与显式转换,定义转换时需要加public static!!!
运算符重载时也是如此,例如:
-
typeof用于了解类的特征
-
switch 不允许从一个分支直接进入另一个分支
-
类class与结构struct的区别:
- class是引用类型,struct是值类型
- struct的无参构造由系统默认提供,不可重定义、删除。
- class一旦定义了任意构造函数,系统就不会提供默认的无参构造,需要用户自己提供
- struct不可声明析构函数,class可以
- struct不可派生其它struct
- struct声明时,不可有初始化语句,如:
-
枚举enum底层类型默认为int,可以更改为其它:
-
枚举类型若表示位域定认,最好加上[Flag]标志。若不加可能会得到不想要的效果,如下:
加上后:
-
不同的枚举类型不能比较,编译错误(哪怕具有相同名字的域)
-
数组一旦创建,大小就固定了
-
数组是引用类型,但其元素可以值类型也可为引用类型
-
交错数组的实例化
-
foreach的迭代变量是只读的
-
数组常用的属性与方法