Learning hard C#学习笔记——读书笔记 02

发布时间 2023-07-17 15:03:25作者: treasureYuki

每每说到类,不得不介绍的就是类的定义,它是一个抽象的概念,它是一个模板,制造对象的模板

1.定义一个类


class Preson {
    // 类的成员变量
}

默认情况下,class关键字没有显式的使用internal修饰符来定义类,但是没有必要这样做,默认的修饰符就是internal


除了internal这个权限修饰符以外,还有以下的权限修饰符

2.类的成员

定义完类之后,还需要再类中定义成员,类的成员包括字段、属性、方法和构造函数等等

类的成员也有权限修饰符

2.1 字段

public class Person {
    // 姓名,类型位字符串
    private string name;
    // 年龄,类型为int类型
    public int age;
    // 性别,类型为bool
    protected bool sex;
}

除了以上的修饰符以外,还可以使用关键字 readonly 和 const 来定义字段,若使用 readonly 修饰字段,则该字段为只读的,若使用 const 修饰字段,则该字段是不可变的


public class Person {
    private readonly string name;
    public const int age = 18;
    protected bool sex ;
}

readonly 与 const 的区别

若使用readonly修饰字段,则不需要在定义时初始化,而是可以在构造函数中完成初始化,但是使用const 进行修饰,则需要在定义时初始化


static 修饰

除了 readonly、const 修饰,还可以是 static 修饰,使用 static 修饰,静态字段必须通过类了进行访问,而实例字段通过对象实例来访问

public class Person {
    public static string name;
    public int age;
}

// 访问方式的不同
new Person().age;
Person.name

2.2 属性

属性是对字段的扩展,是根据OOP思想对字段进行封装的结果,通过这种封装可以防止客户端直接对字段进行篡改

public class Person {
    // 私有字段定义  
    private string name;
  
    public string Name {
        get {
            return name;
        }

        set {
            // value是隐式参数
            name = value;
        }
    }
}

在上段代码中,我们的get、set访问器是可读可写的,但是get、set构造器也可以设置访问权限
public class Person {
    private string name;
    private int age;
    private bool sex;

    public string Name {
        get {
            return name;
        }
        // 私有set访问器
        private set {
            name = value
        }
    }

   
    public int Age {
        // 不包含set访问器,只读属性定义
        get {
            return age;
        }
    }
}

和静态字段类似,属性也可以通过static关键字声明为静态属性,静态属性属于类级别,不能通过类的实例进行访问,也不能在静态属性中使用非静态的字段

public class Person {
    private static string name;

    // 静态属性
    public static string Name {
        get {
            return name;
        }
        set {
            name = value;
        }
    }
}

2.3 方法

方法就和 Java中的方法类似

注意:Main方法是每个C#应用程序的入口点,在启动应用程序时,Main由公共语言运行时(CLR)负责调用

public class Person {
    // 类定义了一个没有返回值的打印方法
    // name是用户传入的参数
    public void Print(string name) {
        Console.WriteLine("输入的值为:"+name);
    }
}
class Program {
    static void Main(string[] args) {
        Person p = new Person();
        p.Print("张三");
    }
}

方法重载

前面提到过 C# 的运算符支持重载,同样 C# 的方法也支持重载,方法重载中可以定义多个名称相同但是,方法签名不同的方法

方法签名不同指的是:

  • 参数的顺序
  • 参数的类型
  • 参数的个数
class Program 
{
    static void Main(string[] args) {
        Person p = new Person();

        p.Print("张三");
        p.Print(18);
        p.Print("张三",18);
    }
}


public class Person
{
    // 打印方法
    public void Print(string name) {
        Console.WriteLine("输入的值:"+name);
    }

    // 这种只是返回值与原有的方法不同的,不属于方法重载
    // 以下的方法会报错
    public int Print(string name) {
        Console.WirteLine("输入的值:"+name);
        return 1;
    }

    // 下面的方法为方法重载
    public void Print(int age) {
         Console.WriteLine("输入的值:"+age);
    }

    public void Print(string name,int age) {
        Console.WirteLine("输入的值为{0},{1}",name,age);
    }
}

2.4 构造函数

构造函数主要用于创建类的实例化对象,当调用构造函数创建一个类的时候,构造函数会为对象分配内存空间,初始化成员变量

构造函数分为实例构造函数和静态构造函数

1.实例化构造函数

class Person {
    private string name;

    // 实例构造函数
    public Person() {
        name = "Learning Hard";
    }
}

实例化构造函数的特点

  • 构造函数必须与类同名
  • 构造函数没有返回值
  • 构造函数可以进行方法重载
  • 如果没有显示的定义一个构造函数,C#编译器会自动生成函数体为空的默认无参构造函数
  • 可以对实例化构造函数指定访问级别

2.静态构造函数

除了实例化构造函数外,构造函数还包括静态构造函数,静态构造函数用于初始化静态成员变量(类似于Java中的静态代码块或者静态内部类)在创建一个实例或引用任何静态成员之前,CLR都将自动调用静态构造函数

class Person 
{
    private static string name;

    static Person() {
        name = "Learning Hard";
    }

    public static string Name
    {
        get { return  name; }
    }
}

class Program 
{
    static void Main(string[] args)
    {
        Console.WriteLine(Person.Name);
        Console.WriteLine(Person.Name);
        Console.Read();
    }
}

静态构造函数的注意点

  • 静态构造函数不能使用任何访问修饰符
  • 静态构造函数不能带有任何的参数
  • 静态构造函数只会执行一次
  • 不能直接调用静态构造函数
  • 在程序中,程序员无法控制静态构造函数的执行时机

2.5 析构函数

析构函数用于在类销毁前释放类实例所使用的托管和非托管资源,对于C#应用程序创建的大多数对象,可以依靠.NET Framework的 GC 来隐式地执行内存管理任务

但是如果封装了非托管的资源对象,在应用程序使用完毕这些非托管资源后,垃圾回收器运行对象的析构函数(Finalize方法)来释放这些资源

class Person {
    // 析构函数
    ~Person() {
        Console.WriteLine("析构函数被调用了")
    }
}

上面的析构函数隐式的调用了基类Object的Finalize方法

protected override void Finalize() 
{
    try
    {
        Console.WriteLine("析构函数被调用了");
    }
    finally
    {  
        // 调用Object的Finalize方法
        base.Finalize();
    }
}

析构函数的注意点

  • 不能在结构体中定义析构函数,只能对类使用析构函数
  • 一个类只能有一个析构函数
  • 无法继承或重载析构函数
  • 无法显式地调用析构函数,析构函数是由垃圾回收机制自动调用的
  • 析构函数既没有修饰符也没有参数

2.6 索引器

当一个类包含数组成员时,索引器的使用将大大简化类对数组成员的访问,索引器的定义类似于属性,也具有get、set访问器

[修饰符] 数据类型 this[索引类型 index]
{
    get { 
        // 返回类中数组某个元素  
    }
    set {
        // 对类中数组元素赋值
    }
}
class Person {
    private int[] intarray = new int[10];

    public int this[int index] {
        get
        {
            return intarray[index];
        }
        set
        {
            intarray = value;
        }
    }
}

class Program 
{
    static void Main(string[] args) 
    {
        Person person = new Perons();
        person[0] = 1;
        person[1] = 2;
        person[2] = 3;
        Console.WriteLine(person[0]);
        Console.WriteLine(person[1]);
        Console.Read()
    }
}


类 与 结构体的区别


  • 语法上的区别,定义类要使用关键字 class,而定义结构体则使用struct
  • 结构体中不可对声明字段进行初始化,但是类可以
  • 如果没有为类显示的定义构造函数,C# 编译器会自动生成一个无参数的实例构造函数,而一旦我们为类定义了一个构造函数,C# 就不再会自动生成隐式构造函数,而结构体中,无论是否显式的定义了构造函数,隐式构造函数一直都存在
  • 结构体不能显示的定义无参数的构造函数,而类可以显示的定义一个无参的构造函数
  • 在结构体的构造函数中,必须要为结构体中的所有字段赋值
  • 创建结构体对象,可以不使用 new 关键字,但此时结构体对象的字段值是没有初始值的,但是类必须使用 new 关键字来创建对象
  • 结构体不能继承结构体或类,但是可以实现接口,而类可以继承类,但是不能继承结构体,它也可以实现接口
  • 类是引用类型,而结构体是值类型
  • 结构体不能定义析构函数,而类可以由析构函数
  • 不能用abstract和sealed关键字修饰结构体