C#笔记——继承和类和方法和接口和转换

本文最后更新于:2023年11月7日 中午

为什么会突然文艺复兴

因为我在写某个泛用模板类的时候发现实现接口的类居然不能转换成接口类型,就是:int类型无法转换为IComparable类型,而隔壁 java 则支持int类型转换为Comparable类型。
后来我直接改为了泛型方法,然后用了约束where T : IComparable<T>解决问题了。
那么感谢隔壁群大佬的解释,“这就是C#的问题了,[]不covariant”。

访问修饰符

访问修饰符包括:privatepublicprotectedinternalprotected internal

修饰符 含义
private 只在类的内部可见
internal 对该程序集内所有类可见
protected 对所有继承该类的类可见
internal protected 对所有继承该类型或在该程序集内声明的类可访问
public 对任何类可访问

静态量

静态量由static修饰。静态字段被类的实例共享,所有实例都访问同一内存位置。因此,如果该内存位置的值被一个实例改变了,这种改变对所有的实例都可见。
访问静态成员有两种方式:

  • 使用类名,如:Math.Sqrt(16)
  • 在调用该成员的类中包含一个using static声明,如:using static System.Math; Sqrt(16);

常量

常量由const修饰符修饰。它们对类的每个实例都是可见的,不需要类的实例也可访问,不能对其进行修改。但常量没有自己的存储位置,会在编译时被编译器替换,类似于 C 与 CPP 的#define宏定义。

公共和私有构造函数,利用this调用其他构造函数

这其实是一个技巧。
第一种应用情况是:public MyClass(int x) : this(x, "Using Default String"),在这里,单参数的构造函数调用了同一个类中具有两个参数的构造函数,并为第二个参数提供了一个默认值。
第二种情况是,一个类有好几个构造函数,并且它们都需要在对象构造的过程中执行一些公共的代码,那我们就可以把这些公共的代码提取出来作为一个私有构造函数,用于被其他构造函数调用,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class MyClass
{
readonly int firstVar;
readonly double secondVar;

public string UserName;
public int UserIdNumber;

private MyClass()
{
firstVar = 20;
secondVar = 30.5;
}

public MyClass(string firstName) : this()
{
UserName = firstName;
UserIdNumber = -1;
}

public MyClass(int idNumber) : this()
{
UserName = "Anonymous";
UserIdNumber = idNumber;
}
}

继承

使用基类的引用

如果有一个派生类对象的引用,就可以获取该对象基类部分的引用(使用类型转换运算符或as运算符把该引用转换为基类类型)。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BaseClass {}
public class DerivedClass : BaseClass {}

public static class Example
{
public static void Main()
{
DerivedClass d = new DerivedClass();
// 类型转换运算符
BaseClass b1 = (BaseClass)d;
// as 运算符
BaseClass b2 = d as BaseClass; // d is BaseClass ? (BaseClass)d : (BaseClass)null
}
}

将派生类对象强制转换为基类对象的作用是产生的变量只能访问基类的成员(在被覆写方法中除外)。

虚方法和覆写方法

虚方法可以使基类的引用访问提升至派生类内。在基类中,使用virtual修饰符修饰的方法为虚方法。在其派生类中,使用override修饰符修饰的方法为覆写方法。
当使用对象基类的引用去调用一个虚方法时,调用的是被标记为override的方法的最高派生的版本。
如果我们不想覆写方法,则可以使用new修饰符修饰方法。

程序集间的引用

要从不同程序集中进行继承,需要添加对包含该基类的程序集的引用,可以在 Visual Studio 工程中的 References 节点中进行添加。注意这里的添加引用不是使用using指令。

抽象类

抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。使用abstract修饰符声明抽象类。关于抽象类,要注意以下几点:

  • 不能创建抽象类的实例。
  • 抽象类可以包含抽象成员或非抽象成员。
  • 抽象类可以派生自另一个抽象类。
  • 其派生类必须实现所有抽象成员,除非它自身也是个抽象类。

抽象成员

抽象成员是指设计为被覆写的函数成员,并且只能在抽象类中声明。用abstract修饰符声明抽象成员。不能有实现代码块,用分号取代。必须被覆写。同样地,也是使用override对抽象成员进行覆写。

密封类

其实就是无法被继承的类。用sealed修饰符声明密封类。

静态类

static修饰符声明静态类。静态类中所有的成员都是静态的,用于存放不受实例数据影响的数据和函数(比如创建一个包含一组数学方法的计算类)。静态类是隐式密封的。

方法

方法签名

方法的签名包括:方法名,参数个数、类型、顺序,参数修饰符;不包括:方法的返回值。

形参

形参是局部变量,它声明在方法的参数列表中,而不是在方法体中。例如:public void PrintSum(int x, int y),其中xy就是形参。

实参

当代码调用一个方法时,形参的值必须在方法的代码开始执行之前初始化。用于初始化形参的表达式或变量称作实参(actual parameter, 有时也称 argument)。例如:PrintSum(5, a),其中5a就是实参。

方法参数修饰符

ref参数修饰符

用于声明引用参数。使用引用参数时,必须在方法的声明和调用中都使用ref修饰符。实参必须是变量,在用作实参前必须被赋值。
引用参数不会在栈上为形参分配内存,而是将形参的参数名作为实参变量的别名,指向相同的内存位置。

out参数修饰符

用于声明输出参数。输出参数用于从方法体内把数据传出到调用代码。必须在方法的声明和调用中都使用修饰符。实参必须是变量,不需要提前赋值。
在方法内部,给输出参数赋值之后才能读取它。这意味着参数的初始值是无关的,而且没有必要在方法调用之前为实参赋值。

in参数修饰符

用于声明输入参数。输入参数用于从调用代码把数据传到方法体内,其传递的是只读引用。可以不在方法的调用中使用修饰符。实参必须是变量。

param参数修饰符

用于声明参数数组。一个参数列表中最多只能有一个参数数组,且必须位于最后。只能声明一维数组类型。

接口

接口是指定一组函数成员而不实现它们的引用类型。所以只能由类和结构来实现接口。
接口不能包含数据成员和动态成员。接口成员是隐式 public 的,不允许有任何访问修饰符,包括 public。
如果一个类实现了多个多个接口,并且其中的一些接口成员具有相同的签名和返回类型,那么类可以实现单个成员来满足所有包含重复成员的接口;也可以通过接口名和.运算符来创建显式接口成员实现,但是这样做的话,就不存在类级别的接口方法了,也就是说要调用这个接口方法的话,只能将类的实例的引用转换成接口的引用了(当然你也可以再实现一个类级别的相同方法)。

转换

隐式引用转换

  • 所有引用类型可以被隐式转换为object类型。
  • 任何接口可以隐式转换为它继承的接口。
  • 类可以隐式转换为它继承链中的任何类,或者是实现的任何接口。

装箱转换

装箱(boxing)转换是一种隐式转换,它接受值类型的值,根据这个值在堆上创建一个完整的引用类型对象并返回对象引用。在装箱之后,值有两份副本——原始值类型和引用类型副本,每一个都可以独立操作(也就是你改变值类型的值不会影响引用类型的值,同样的,改变引用类型的值不会影响值类型的值)。
任何值类型ValueTypeS都可以被隐式转换为objectSystem.ValueTypeInterfaceT类型(如果ValueTypeS实现了InterfaceT)。

拆箱转换

拆箱(unboxing)是把装箱后的对象转换回值类型的过程,是显示转换。如果尝试将一个值拆箱为非原始值类型的话,程序会抛出InvaildCastException异常。


这里有一只爱丽丝

希望本文章能够帮到您~


C#笔记——继承和类和方法和接口和转换
https://map1e-g.github.io/2023/11/04/CSharp-learning-10/
作者
MaP1e-G
发布于
2023年11月4日
许可协议