C#

结构体(Struct)

概念: C#的结构体类型(或称为结构)是用户自定义类型的一种, 它为用户将实际应用中数据类型不同,但互相联系的数据看作 一个整体提供了类型支持。

语法:

访问修饰符 struct 结构体类型名
{
	 //各种成员...:
	 
	 //成员定义格式为: 访问修饰符 成员类型 成员名称
	 //例:
	 public string name;
	 int age;
}

C#中结构体的特点

结构可带有方法、字段、索引、属性、运算符方法和事件。
结构可定义构造函数,但不能定义析构函数。但是,您不能为结构定义无参构造函数。无参构造函数(默认)是自动定义的,且不能被改变。
与类不同,结构不能继承其他的结构或类。
结构不能作为其他结构或类的基础结构,不能做父类。
结构可实现一个或多个接口。
结构成员不能指定为 abstract、virtual 或 protected。
当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 New 操作符即可被实例化。
如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用。

类与结构体的不同

类和结构有以下几个基本的不同点:

类是引用类型,在堆中分配空间存储数据,在栈中存放引用即堆中数据的地址;结构是值类型,在栈中分配空间存储数据。
结构不支持继承。
结构不能声明默认的构造函数;因为无参构造函数是结构体预定义的;不允许被重新定义的;并且他会一直存在。
结构体中声明的非常量字段无法赋予初值,类可以。
结构体的构造函数中,必须为结构体所有字段赋值,类的构造函数无此限制。
	例:
    struct Test1
    {
        private  int i;
        public Test1(int j)
        {
            this.i = j;
        }//若不对i进行赋值,则报错:CS0171:在控制返回调用方之前,字段“Test1.i”必须完全赋值
    }
结构体的静态构造函数不能有访问修饰符和参数,
静态构造函数会在使用带参构造函数进行初始化时被自动调用或访问静态数据成员时调用;且只被调用一次。

类与结构体的选择

1、当堆栈的空间很有限,且有大量的逻辑对象时,创建类要比创建结构好一些;

2、对于点、矩形和颜色这样的轻量对象,假如要声明一个含有许多个颜色对象的数组,则CLR需要为每个对象分配内存,在这种情况下,使用结构的成本较低;

3、在表现抽象和多级别的对象层次时,类是最好的选择,因为结构不支持继承;

4、大多数情况下,目标类型只是含有一些数据,或者以数据为主,则使用结构更好一些**;**

索引器

运算符重载

语法: public static 类 operator 运算符 (参数列表) 例:

class Fraction
{
	int num;
	int den;
	public Fraction(int numerator, int denominator)
    {
        if (denominator == 0)
        {
            throw new ArgumentException("除数不能为0", nameof(denominator));//throw 抛出异常
        }
        num = numerator;
        den = denominator;
    }
	public static Fraction operator *(Fraction a,Fraction b) => new Fraction(a.num * b.num, a.den * b.den);//对乘法运算符进行重载
    public static Fraction operator /(Fraction a, Fraction b)
    {
        if (b.num == 0)
        {
            throw new DivideByZeroException();
        }
        return new Fraction(a.num * b.den, a.den * b.num);
    }///对除法运算符进行重载
}

alt

alt

扩展方法

MSDN:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/extension-methods#binding-extension-methods-at-compile-time

定义

语法: public static 返回值类型 方法名(this 被扩展类型 类型名,其他参数...)

1. 扩展方法本身也必须声明为static;
2. 声明扩展方法的类必须为static类,而普通的静态方法所在的类可以是静态也可以是非静态;
3. 扩展方法的访问修饰符必须是public;
4. 扩展方法必须包含关键字this作为第一个参数类型,并在后面跟着它所扩展的类型的名称。

扩展方法使你能够向现有类型(this所修饰的第一个参数类型--被扩展类型)“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种静态方法,可以以静态方法调用(直接通过扩展方法所在的静态类来调用),也可以以实例方法调用(通过扩展类型(this所修饰的类型)调用

在代码中,使用实例方法语法调用该扩展方法。 编译器生成的中间语言 (IL) 会将代码转换为对静态方法的调用。 并未真正违反封装原则。

在编译时绑定扩展方法

可以使用扩展方法来扩展类或接口,但不能重写扩展方法。与接口或类方法具有相同名称和签名的扩展方法永远不会被调用。 编译时,扩展方法的优先级总是比类型本身中定义的实例方法低。换句话说,如果某个类型具有一个名为 Process(int i) 的方法,而你有一个具有相同签名的扩展方法,则编译器总是绑定到该实例方法。当编译器遇到方法调用时,它首先在该类型的实例方法中寻找匹配的方法。如果未找到任何匹配方法,编译器将搜索为该类型定义的任何扩展方法,并且绑定到它找到的第一个扩展方法

1.扩展方法能访问它们所扩展的类型中的public成员,但扩展方法无法访问它们所扩展的类型中的专用变量(因为扩展对象不能破坏封装的概念)
3.如果扩展方法与该类型中定义的方法具有相同的签名,则扩展方法永远不会被调用。
4.扩展方法的作用域是整个命名空间可见。 例如,如果你在一个名为 Extensions 的命名空间中具有多个包含扩展方法的静态类,则这些扩展方法将全部由 using Extensions; 指令置于范围中。

Const

MSDN:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/const

使用 const 关键字来声明某个常量字段或常量局部变量。 常量字段和常量局部变量不是变量并且不能修改。 常量可以为数字、布尔值、字符串或 null 引用。 不要创建常量来表示你需要随时更改的信息。

alt

readonly(动态常量)与const(静态常量)

学习链接

readonly:

1.readonly允许把一个字段设置成常量,但可以执行一些运算,可以确定它的初始值。

2.readonly是在计算时执行的,所以它可以用某些常量初始化。

3.readonly是实例成员,所以不同的实例可以有不同的常量值,这使readonly更灵活。

-------------------------------------------------------------------------------------------------

const字段只能在该字段的声明中初始化。

readonly字段可以在声明或构造函数中初始化,因此,根据所使用的构造函数,readonly字段可能具有不同的值。

const字段是编译时常量,而readonly字段可用于运行时常量。

public static readonly uint a1 = (uint)datetime.now.ticks;

3.const默认就是静态的,而readonly如果设置成静态的就必须显示声明。

4.const对于引用类型的常数,可能的值只能是string和null。readonly可以是任何类型。

常量对象

参考:https://blog.csdn.net/feitianxuxue/article/details/7311055

常量对象与常量成员函数来防止修改对象,实现最低权限原则。

可以用关键字const来指定对象是不可修改的,任何修改该对象的企图,都会导致编译错误