C#常识篇(二)
委托和事件的区别
委托可以认为是对指定函数类型的引用,通过委托可以实现将函数作为参数或者变量进行使用,委托是类型安全的,仅指向与其声明时指定函数类型相匹配的函数。委托可以分为单播委托和多播委托,二者的区别在于是对单个方法还是一组方法的引用,多播委托则可以通过特定的符号"+="和"-="来完成函数的订阅和取消订阅。
事件则是一种特殊的多播委托,其相比于普通的多播委托更加安全,事件将多播委托的调用权限隔离在其所在类的内部,并对外部关闭了直接通过赋值符号"="修改多播委托实例的入口,使得外部调用者仅能够进行基本的函数订阅和取消订阅的操作。
多播委托的底层实现
多播委托实际上是一个类实例,其中定义了一个函数引用列表用于存储订阅的函数。当调用多播委托时,将由实例内部来遍历该函数引用列表,并按照订阅顺序依次调用函数,CLR提供底层支持。
重载和重写的区别
重载是一种编译时多态,重载函数的名称相同但参数列表不同,在调用时编译器会自动根据传递的参数列表适配指定形式的重载函数。
重写是一种运行时多态,子类重写父类的方法,在调用时根据实例对象的类型而适配重写函数。
for和foreach的区别
对于数组遍历,for循环通过数组索引直接访问元素,而foreach循环通过获取集合的枚举器(IEnumerator)来顺序遍历元素。foreach循环中的迭代变量是只读的,不能直接赋值,但可以修改引用类型对象的属性。在foreach遍历过程中修改集合(如添加、删除元素)会导致迭代器失效,抛出异常。在选择循环方式时,应根据具体需求决定:需要索引访问或修改集合时使用for循环,只读遍历且代码简洁性更重要时使用foreach循环。
类和结构体的区别
1. 类的实例对象存储在堆上,其引用存储在栈上;结构体的存储位置取决于上下文:局部变量在栈上,作为类的字段或数组元素时在堆上,装箱时也在堆上。
2.类支持完整的面向对象特性(继承、多态、封装),可以实现接口;结构体不能继承其他类型,只能实现接口。
3.类是引用类型,多个变量可以引用同一对象,修改通过任一引用进行会影响所有引用;结构体是值类型,赋值操作创建副本,各个副本相互独立。
4.类适合大型对象、需要复杂行为或继承多态的场景;结构体适合轻量级的数据结构,通常设计为不可变,主要存储数据而非提供复杂行为。此外,结构体不能显式定义无参构造函数,不能有字段初始化器,且要避免设计过大的结构体以防止复制开销。
foreach的原理
foreach是一种结合迭代器模式遍历实现了IEnumerable(或IEnumerable<T>)接口的容器类的语句,这两个接口会提供获取IEnumerator(或IEnumerator<T>)类型迭代器的方法,foreach会在运行时自动获取迭代器并启动对容器类的遍历。
协变和逆变
协变允许将派生程度更大的类型赋值给派生程度更小的变量,逆变允许将派生程度更小的类型赋值给派生程度更大的变量。协变和逆变为数组、委托和泛型接口提供类型参数的灵活转换。
在C#中,通过
out关键字声明协变泛型参数(只能出现在输出位置,如返回值),通过in关键字声明逆变泛型参数(只能出现在输入位置,如方法参数)。协变性体现在方法的返回类型上,逆变性体现在方法的参数类型上。需要注意的是,数组协变是C#的语言特性但存在运行时类型安全问题,而泛型接口的协变和逆变在编译时和运行时都是类型安全的。
结构体
结构体隐式继承自System.ValueType,属于值类型。结构体的存储位置取决于上下文:局部变量在栈上,但作为类的字段、数组元素或装箱时存储在堆上。
结构体的主要特性:
支持实现接口,可以重写Equals、ToString、GetHashCode方法,但不支持实现继承和多态;
不能包含自身类型的实例字段(防止无限递归),但可以包含引用类型的集合;
可以不使用new关键字创建实例,但使用前必须显式初始化所有字段;
C# 10之前自定义构造函数会隐藏默认无参构造函数,C# 10及以后支持自定义无参构造函数;
自定义构造函数必须初始化所有字段;
结构体适合表示轻量级的数据结构,设计上偏向数据而非复杂行为。结构体赋值会进行值复制,对于大型结构体可能产生性能开销。建议使用readonly struct和init关键字来创建不可变结构体。
常见修饰符
(1)public:能够在其声明所在的类或结构体甚至程序集外部访问,程序集外部访问需要引用其所在程序集;
(2)protected:能够在直接或间接派生类或其声明所在的类中访问;
(3)private:仅能够在其声明所在的类或结构体中访问;
(4)internal:能够在其声明所在的类或结构体之外但在所处程序集之内访问;
值得注意的是,未明确指定访问修饰符的类或结构体或接口默认为internal,未明确指定访问修饰符的类成员或结构体成员默认为private,接口成员默认为public。
静态构造函数
静态构造函数用于初始化任何静态数据,或执行仅需执行一次的特定操作。 将在创建第一个实例或引用任何静态成员之前自动调用静态构造函数。 静态构造函数最多调用一次。如果静态字段的初始化并不复杂或者不涉及一些特殊的处理,那么建议在静态字段声明时即进行初始化,使用静态构造函数会影响运行时优化。常见的应用场景包括日志记录、创建非托管代码的包装类以及运行时的参数类型检查。
如果这篇文章对你有帮助,请给作者点个赞吧!


浙公网安备 33010602011771号