欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

C# - 刘铁萌笔记

最编程 2024-10-18 09:59:00
...

C#——刘铁猛笔记


类、名称空间(简述)

类(class)是构成程序的主体

名称空间(namespace)以树形结构组织类(其他类型)

名称空间:名称空间是用来组织和管理类、接口、结构体等类型的逻辑容器。它可以帮助开发人员避免命名冲突,将相关的类型组织在一起,提高代码的可读性和可维护性。名称空间可以嵌套使用,形成层次结构。在C#中,使用namespace关键字来定义名称空间。

打个比方:

在一个图书馆中,每本书都会有它专属的类放置在一个特定的位置上,那么名称空间就相当于一个图书馆,类就相当于书的类。不同的图书馆有些书的类一样,但其所含书本不一样,因此名称空间也能防止类的名字冲突。

当程序中的一个名称空间中,想要使用另一个名称空间的某一个方法时,需要在程序中名称空间的外部使用using引用所需的名称空间,或者在该名称空间中写出所需名称空间的限定名称。

namespace HelloWorld
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");

        }
    }
}

当引用的名称空间中有两种或两种以上的名称空间中包含同名的类,使用这个类时也需要写出限制性名称。

类库

类库引用时使用名称空间的物理基础,使用名称空间时,可以点开References(引用)来检查是否有包含该名称空间的类库(类库简称dll)

在C#中,类库(Class Library)指的是一组封装了一些相关功能的类、接口和其他类型的集合。类库通常用于封装通用的功能,以便在多个应用程序中重复使用。

类库可以包含各种类型的功能,例如数据结构、算法、I/O操作、网络通信、图形界面控件等。C#本身提供了一些标准的类库,如.NET Framework中的类库,它包含了大量的基本功能和工具类,用于支持C#程序的开发。

除了使用.NET Framework提供的类库之外,开发人员也可以自己编写类库,以封装自己的功能并在不同的项目中重复使用。这样可以提高代码的复用性,降低开发成本,同时也有利于代码的维护和管理。

在C#中,类库通常以DLL(Dynamic Link Library)的形式存在,可以通过引用的方式在C#项目中使用。通过引用类库,开发人员可以轻松地使用其中封装的功能,而无需关心具体的实现细节,从而提高开发效率并降低代码的重复编写。

一个类库可以包含一个或多个名称空间,用于组织其中的类型。

黑盒引用(无法看源代码)

黑盒引用(Black Box Reference):指的是只能通过接口或基类来引用对象的方式。在黑盒引用中,只能访问对象的公共成员或通过接口暴露的方法,而无法直接访问对象的私有成员或实现细节。这种引用方式更加封装和安全,符合面向对象编程的封装原则。
  • 引用一个类库时,右击reference

  • 引用一个类库时,它可能受限于更底层的类库,因此引用时还需要引用其更底层的类库,为了避免这个麻烦,可以引用NuGet,类似于一个类库包。

白盒引用

白盒引用(White Box Reference):指的是可以直接访问对象的私有成员或实现细节的引用方式。在白盒引用中,可以直接访问对象的所有成员,包括私有成员和实现细节。这种引用方式可能会破坏对象的封装性,增加耦合度,不利于代码的维护和扩展。

引用自己已有的项目,此时需要将该项目添加到自己的solution里面,之后再右击reference,再solution里勾选该类库。

image-20240915215719823

图中有新建项和现有项,若要使用新建项,在打开时就要选择class library项目。

总的来说,黑盒引用更符合面向对象编程的封装和抽象原则,而白盒引用则更容易导致代码的耦合和依赖性。在设计和编写代码时,应该尽量使用黑盒引用,只暴露必要的接口和方法,避免直接暴露对象的内部实现细节。

依赖关系

在C#中,依赖关系指的是一个类或对象在实现功能时依赖于另一个类或对象的情况。依赖关系是面向对象编程中的一个重要概念,它描述了一个对象使用另一个对象提供的功能或服务的情况。

依赖关系通常体现在类之间的关联或调用关系上。当一个类需要使用另一个类的功能时,它就会依赖于这个类。这种依赖关系可以通过构造函数注入、属性注入或方法参数传递等方式来实现。

依赖关系的存在可以带来一些好处,如提高代码的复用性、降低耦合度、便于单元测试等。但如果依赖关系设计不当,可能会导致代码的脆弱性、难以维护和扩展等问题。

在实际开发中,我们通常会借助依赖注入(Dependency Injection)等技术来管理和解决类之间的依赖关系,以提高代码的可维护性和灵活性。通过合理设计和管理依赖关系,可以使代码更加模块化、可测试和可扩展。

类、对象、类与对象的关系

类(Class)是一种模板或蓝图,用于描述对象的属性和行为。类定义了对象的结构和行为,包括属性(字段)和方法。在C#中,类是定义对象的基本单位,通过类可以创建对象的实例。

对象

在C#中,对象(Object)是类的实例化(Instance)结果,是内存中的一个具体实体,具有属性和行为。对象是类的具体化,通过实例化类可以创建对象。对象在内存中占据一定的空间,包含了类中定义的属性和方法的具体数值和实现。

类与对象的关系

image-20240916101828223

  • 类是对象的模板,定义了对象的属性和行为,描述了对象的结构。
  • 对象是类的实例化结果,是类的具体实体,具有类定义的属性和行为。
  • 通过类可以创建多个对象,每个对象都是类的一个实例,但它们在内存中是独立存在的,各自拥有自己的属性值。
  • 类定义了对象的结构和行为,而对象是类的具体化,实际应用中我们操作的是对象。

简单来说,就是你自己写了一个类,这个类就像一个概念一样,它包含着一些用来形容这个概念的东西,你想要使用这个类时候,你就需要创建一个对象,之后用这个对象来进行你后续的操作。

比如你写了一个飞机的类,但这个类是个概念,你能开概念吗?肯定不行,你要开的是飞机这个机器,所以你就要创建一个飞机开。

类的三大成员

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

前三个后面会详细说明

小知识–使用MSDN文档

把光标移到你所使用的类上,按f1键

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

静态成员与非静态成员

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

静态成员是属于类的成员,而不是属于类的实例(对象)的成员。静态成员可以被类的所有实例共享,可以通过类名直接访问,不需要创建类的实例。在内存中,静态成员只有一份拷贝,被所有实例共享。

非静态成员则是属于类的实例(对象)的成员,每个对象都有自己的一份。非静态成员需要通过对象来访问,每个对象都有自己的非静态成员的拷贝。

构成c#的语言的基本元素:关键字,操作符,标识符,标识符号,文本,注释。

小知识–声明变量和类时名称的要求

声明变量时变量名采用驼峰命名法,即首单词字母要小写,后续单词字母大写

声明一个类时名称时,单词都要大写

类型

什么是类型

image-20240916102321322

强类型语言、弱类型语言

强类型指的是声明的变量是什么类型,以后它就是什么类型,弱类型指声明的变量可以是多个类型

  • 强类型语言是指在编程时要求严格定义变量的数据类型,不允许不同类型之间的隐式转换。

  • 在强类型语言中,变量的数据类型必须在编译时就确定,并且不会发生自动类型转换。

  • 强类型语言通常具有更严格的类型检查,能够在编译时捕获一些潜在的类型错误。

  • 弱类型语言是指在编程时对变量的数据类型要求较为灵活,允许不同类型之间的隐式转换。

  • 在弱类型语言中,变量的数据类型通常可以在运行时动态确定,允许自动类型转换。

  • 弱类型语言通常具有更灵活的类型系统,但也可能导致一些难以发现的类型错误。

小知识–c#中用dynamic定义的变量可以让该变量在不同时刻赋任何类型的值。

namespace DynamicSample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            dynamic myVar = 100;
            Console.WriteLine(myVar);
            myVar = "Mr.Okay!";
            Console.WriteLine(myVar);
        }
    }
}

image-20240916102509962

类型在C#语言中的作用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

C#语言的类型系统–五大数据类型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

变量

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

变量是以变量名为对应的内存地址为起点,以其数据类型所要求的存储空间为长度的一块内存区域(变量名中要么存储的是值类型的值,或者引用类型的地址)。

静态变量

  • 静态变量属于类,而不是类的特定实例。
  • 静态变量在类加载时被初始化,只有一份存储空间,所有类的实例共享同一份静态变量。
  • 静态变量使用static关键字声明,在内存中存储在静态存储区域。
  • 可以通过类名直接访问静态变量,无需实例化对象。
  • 通常用于存储类级别的信息,如常量、计数器等。
  • image-20240916102721417

实例变量

  • 实例变量属于类的实例(对象),每个对象都有自己的实例变量副本。
  • 实例变量在创建对象时被分配内存空间,并随着对象的销毁而释放。
  • 实例变量不使用static关键字声明,每个对象都有自己的实例变量。
  • 必须通过对象实例来访问实例变量。

image-20240916102747541

数组元素

数组元素(Array Element):数组是一种数据结构,用于存储相同类型的多个元素。数组元素指的是数组中的单个数据项,通过索引访问。例如,对于整型数组 int[] numbers = {1, 2, 3, 4, 5};,numbers[0] 表示数组 numbers 中的第一个元素,其值为 1。

值参数

值参数(Value Parameter):值参数是一种参数传递方式,将参数的值传递给方法。在方法内部对值参数的修改不会影响到原始值。在方法签名中,参数前没有 ref 或 out 关键字的参数均为值参数。

引用参数

引用参数(Reference Parameter):引用参数是一种参数传递方式,将参数的引用传递给方法,使得在方法内部对参数的修改会影响到原始值。在方法签名中,参数前加上 ref 关键字表示引用参数。

输出参数

输出参数(Output Parameter):输出参数是一种特殊的引用参数,用于从方法中返回多个值。在方法签名中,参数前加上 out 关键字表示输出参数,调用方法时必须为输出参数赋值。

局部变量

局部变量(Local Variable):局部变量是在方法、构造函数或代码块内部声明的变量,只在声明的作用域内有效。局部变量在声明时必须初始化,可以在声明时或稍后赋值。

值类型、引用类型声明的变量在内存中的分配

值类型在内存分配的时候会根据该类型所需的空间来分配字节,引用类型在内存分配的时候只会分配四个字节,没有引用实例时该字节的二进制都是零,当创建实例后会把堆内存的地址放进该变量中,在堆内存中分配空间时才会计算该变量真正所需要的空间。

变量的默认值

成员变量分配好空间后不赋值时会自动都赋值成0,但本地变量不赋值会报错。

装箱和拆箱

装箱:当一个引用类型的变量发现赋予的值时放在栈上的值类型的值,那么其会在堆中的一个空位置上存储这个值,并把该位置的地址赋值给引用类型的变量上。

  • 装箱是将值类型转换为引用类型的过程。

  • 当将值类型赋值给一个对象类型(如 object)时,会发生装箱操作,将值类型的数据包装在一个堆分配的对象中。

  • 装箱会导致性能开销,因为需要在堆上分配内存来存储值类型的数据,并且需要进行数据复制。

拆箱:当一个值类型的变量发现赋予的值时引用类型,那么其会根据地址找到该值赋给自己

  • 拆箱是将引用类型转换为值类型的过程。
  • 当从对象类型(如 object)中获取值类型数据时,需要进行拆箱操作,将包装在对象中的数据提取出来转换为值类型。
  • 拆箱需要进行类型检查和数据复制,可能会导致性能开销。

方法

方法的由来

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

方法的声明与调用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传当一个函数以类的成员出现时简称为方法

构造器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

什么是构造器

在 C# 中,构造器(Constructor)是一种特殊类型的方法,用于在创建类的实例(对象)时初始化对象的状态。构造器的作用是初始化对象的成员变量、属性或执行其他必要的初始化操作。以下是构造器的一些特点:

命名与特点:

  1. 构造器的名称与类名相同。

  2. 构造器没有返回类型,包括 void。

  3. 构造器可以重载,即同一个类可以有多个不同参数列表的构造器。

初始化对象:

  1. 在实例化对象时,构造器会被自动调用,用于初始化对象的状态。
  2. 构造器可以初始化对象的成员变量、属性,或执行其他必要的初始化操作。

默认构造器:

  1. 如果没有显式定义构造器,C# 会提供一个默认构造器(无参数构造器),用于初始化对象。
  2. 如果显式定义了带参数的构造器,但没有定义无参数构造器,那么默认构造器就不会被提供。

对象的创建

当通过 new 关键字创建一个类的实例时,会在内存中分配一块空间来存储该对象的数据。这个空间包括对象的字段、属性和方法等信息。

构造器的调用

在对象创建的过程中,会调用构造器来初始化对象的各个部分。构造器可以有多个重载形式,根据参数列表的不同进行选择调用。

内存分配

在调用构造器之前,系统会先为对象分配内存空间。构造器负责对这块内存空间进行初始化,包括对字段、属性等成员变量的赋值操作。

构造器的执行顺序

如果类的继承关系中包含父类和子类,构造器的执行顺序是先执行父类的构造器,然后执行子类的构造器。这确保了对象的所有部分都能得到正确的初始化。

构造器的作用

构造器可以用来初始化对象的状态,进行一些必要的设置操作,确保对象在创建后处于一个合理的状态。

快捷生成构造器

//创建构造器快捷键
//ctor 按两下tab键
public Student(int initId,string initName)
{
        
}

声明无参数构造器(也可以称作默认构造器)

namespace ConstructorExample
{
     class Program
    {
        static void Main(string[] args)
        {
          
            Student stu2 = new Student();
            Console.WriteLine(stu2.ID);
            Console.WriteLine(stu2.Name);


        }
    }

    class Student
    {

        //创建构造器快捷键
        //ctor 按两下tab键
  

        public Student()
        {
            this.ID = 1;
            this.Name = "No name";
        }

        public int ID;
        public string Name;
    }
}

声明带参数的构造器(实例化一个对象时必须对构造器内的成员赋值)

为了防止声明一个类时忘记对其字段赋值,可以使用带参数的构造函数。

namespace ConstructorExample
{
     class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student(2,"Mr.Okay");
            Console.WriteLine(stu.ID);
            Console.WriteLine(stu.Name);
        }
    }

    class Student
    {

        //声明
        public Student(int initId, string initName)
        {
            this.ID = initId;
            this.Name = initName;
        }

        public int ID;
        public string Name;
    }
}

重载

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

什么是重载

方法的重载(Method Overloading)是指在同一个类中可以定义多个具有相同名称但参数列表不同的方法。通过方法重载,可以在同一个类中使用相同的方法名来执行不同的操作,根据传入的参数列表的不同来确定调用哪个方法。

比如Console.WriteLine就是一种重载,可以输出多种类型

小知识–Debug操作

image-20240916104259609

设置断点,当摁f5时会在设置的断点处停止运行

image-20240916104350445

此时调用堆栈中第一行是该函数,第二行是调用该函数的位置

Step-in是一个语句一个语句进行,Step-over是若该断点在是一个调用函数,直接返回调用函数后的结果,Steo-out是寻找调用该函数的地方。

操作符

image-20240916113134508

越靠上的操作符优先级越高,越靠下的操作符优先级越低

什么是操作符?

image-20240916114244570

操作符不能脱离与它关联的数据类型

namespace CreateOperator
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int x = 5;
            int y = 4;
            int z = x / y;
            Console.WriteLine(z);

            double a = 5.0;
            double b = 4.0;
            double c = a / b;
            Console.WriteLine(c);
        }
    }
}

演示结果

2
Mr.Okay
请按任意键继续. . .

简记法

namespace CreateOperator
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Person person1 = new Person();
            Person person2 = new Person();
            person1.Name = "Dear";
            person2.Name = "Dear's wife";
            List<Person> nation = person1+ person2;
            foreach(var p in nation)
            {
                Console.WriteLine(p.Name);
            }

        }
    }

    //创建一个类型
    class Person
    {
        public string Name;
        //public static List<Person> GetMarry(Person p1, Person p2)
        //简记法————如下
        public static List<Person> operator +(Person p1, Person p2)
        {
            List<Person> people = new List<Person>();
            people.Add(p1);
            people.Add(p2);
            for(int i = 0; i < 11; i++)
            {
                Person child = new Person();
                child.Name = p1.Name + "&" + p2.Name + "s child";
                people.Add(child);
            }
            return people;

        }
    }
}

演示结果

Dear
Dear's wife
Dear&Dear's wifes child
Dear&Dear's wifes child
Dear&Dear's wifes child
Dear&Dear's wifes child
Dear&Dear's wifes child
Dear&Dear's wifes child
Dear&Dear's wifes child
Dear&Dear's wifes child
Dear&Dear's wifes child
Dear&Dear's wifes child
Dear&Dear's wifes child
请按任意键继续. . .

优先级与运算顺序

image-20240916144610936

同优先级操作符从左到右

列如:

namespace OperatorPriority
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int x;
            x = 3 + 4 + 5;
            Console.WriteLine(x);
        }
    }
}

结果

12
请按任意键继续. . .

带有赋值功能的操作符的运算顺序都是从右向左

代码:

namespace OperatorPriority
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int x = 100;
            int y = 200;
            int z = 300;
            x += y += z;

            Console.WriteLine(x);
            Console.WriteLine(y);
            Console.WriteLine(z);
        }
    }
}

600
500
300
请按任意键继续. . .

外层命名空间的子集命名空间(静态成员属于类)

各个操作符的作用

x.y 操作符:访问外层名称空间中的子名称空间,访问名称空间中的类型,访问类型中的静态成员,访问对象的成员。

//System.IO.File.Create("D:\\HelloWorld.text");
Form myForm = new Form();
myForm.Text = "Hello,World!";
myForm.ShowDialog();

f(x) 操作符:方法调用

a[x] 操作符:元素访问操作符

new 操作符:在内存中构造一个类型的实例,并且立刻调用这个实例的实例构造器(或初始化器),如果在new操作符有赋值符号时,会把该实例的内存地址赋给该变量。

在C#中,并不是构造类型时都需要使用new操作符,c#会在常用的类型构建实例时省去new操作符。

new操作符也可用于方法的继承和派生(让子类隐藏父类)

typeof 操作符:查看一个类型的内部结构

enum 枚举类型

default 操作符:获取一个类型的默认值

checked 操作符:用于检测程序运行时有没有类型溢出

unchecked 操作符:不检测程序运行时有没有类型溢出

checked和unchecked不仅有操作符用法,还有上下文用法

delegate 操作符:一般用于委托,在当作操作符时是用来声明匿名方法,该方法是只执行一次

委托——代码演示:

namespace OperatorsExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //委托
            Calculator c = new Calculator();
            Action myAction = new Action(c.PrintHello);
            myAction();

        }
    }

    class Calculator
    {
     
        public void PrintHello()
        {
            Console.WriteLine("Hello");
        }
    }
}


结果:Hello

default类型

image-20240916203127566

var关键字 功能 帮忙声明隐式声明变量

namespace OperatorsExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //Dictionary类里的value值类型是Student
            Dictionary<string,Student> stuDic = new Dictionary<string, Student>();
            for(int i = 1;i<= 100; i++)
            {
                Student stu = new Student();
                stu.Name = "s_" + i.ToString();
                stu.Score = 100 + i;
                stuDic.Add(stu.Name, stu);
            }

            Student number6 = stuDic["s_6"];
            Console.WriteLine(number6.Score);

        }
    }


    class Student
    {
        public string Name;
        public int Score;
    }
}

new 操作符:在内存中构造一个类型的实例,并且立刻调用这个实例的实例构造器(或初始化器),如果在new操作符有赋值符号时,会把该实例的内存地址赋给该变量。

new操作符和var关键字的用法:是为匿名类型创建对象,用隐式类型变量来引用实例

namespace OperatorsExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Form myForm = new Form() { Text = "Hello" };
            //是为匿名类型创建对象,用隐式类型变量来引用实例
            var person = new { Name = "Qi", Age = 21 };
            Console.WriteLine(person.Name);
            Console.WriteLine(person.Age);
            Console.WriteLine(person.GetType().Name);

        }
    }
}

拓展类与类之间的继承

namespace OperatorsExample
{
     class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student();
            stu.Report();
            CsStudent csStu = new CsStudent();
            csStu.Report();
        }
    }

    class Student
    {
        public void Report()
        {
            Console.WriteLine("I'm a student");
        }
    }

    class CsStudent : Student
    {
        //重写 子类隐藏父类
        new public void Report()
        {
            Console.WriteLine("I'm CS student");
        }
    }
}

image-20240917205144654

检查异常

image-20240917205317279

delegate 操作符:一般用于委托,在当作操作符时是用来声明匿名方法,该方法是只执行一次

image-20240918142917608

改进为

image-20240918143100492

->操作符

image-20240918144338378

image-20240918144910394

下去自己练习

image-20240918150238104

类型转换

image-20240918223841804

sizeof 操作符:用于查询结构体类型的字节长度,其也可以检测自定义的结构体类型的字节长度,但要放在不安全的上下文中。

& 操作符:取地址操作符,取某个对象的地址。

  • 操作符:找到指针所指向的变量

~ 操作符:取反操作符。

  • 操作符:取反操作后+1。

(T)x 操作符:强制转换操作符

<< >> 操作符:<< 操作符补进来的都是0,>>操作符正数补进来的是0,负数补进来的是1
is 操作符:用于检测一个对象是不是某一类型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student();
            bool res = stu is Student;
            Console.WriteLine(res);//True
 
            Student stu2 = null;
            res = stu2 is Student;
            Console.WriteLine(res);//false
 
            res = stu is Animal;
            Console.WriteLine(res);//True
 
            Human human = new Human();
            res = human is Student;
            Console.WriteLine(res);//False
        }
    }
 
    class Animal
    {
        public void Eat()
        {
            Console.WriteLine("Eating...");
        }
    }
 
    class Human : Animal
    {
        public void Think()
        {
            Console.WriteLine("Thinking...");
        }
    }
 
    class Student:Human
    {
        public void Study()
        {
            Console.WriteLine("I want to play");
        }
    }
 }

as 操作符(强制类型转化)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            object o = new Student();
            Student stu = o as Student;//如果o是Student类型,那么就把该地址赋值给stu,否则赋值null;
            if(stu!=null)
            {
                stu.Study();
            }
        }
    }
 
    class Animal
    {
        public void Eat()
        {
            Console.WriteLine("Eating...");
        }
    }
 
    class Human : Animal
    {
        public void Think()
        {
            Console.WriteLine("Thinking...");
        }
    }
 
    class Student:Human
    {
        public void Study()
        {
            Console.WriteLine("I want to play");
        }
    }
 }

?? 操作符:通常情况下值类型不能赋值null,但Nullable<数据类型>声明的变量可以,或者在值类型后加一个? 。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Nullable<int> x = null;
            int? y = null;
            Console.WriteLine(x);
            Console.WriteLine(x.HasValue);
 
            x = x ?? 1;//如果x是null值,那就把x赋值成1;
            Console.WriteLine(x);
 
 
        }
    }
 
 }

?: 操作符:if,else的简写

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int x = 80;
            string s = String.Empty;
            s = x >= 60 ? "Pass" : "False";//如果真就返回:前面的值,否则返回:后面的值
            Console.WriteLine(s);
 
        }
    }
 
 }

类型转换

image-20240918224028553

隐式类型转换

代码演示:

namespace ConversionExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int x = int.MaxValue;
            //小往大转就隐式
            long y = x;
            Console.WriteLine(y);
        }
    }
}

结果

2147483647
请按任意键继续. . .

image-20240918225344122

子类向父类转换

namespace ConversionExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Teacher t = new Teacher();
            Human h = t;
            Animal a = h;
            a.Eat();
        }
    }

    class Animal
    {
        public void Eat()
        {
            Console.WriteLine("Eating...");
        }
    }

    class Human : Animal
    {
        public void Think()
        {
            Console.WriteLine("Who I am?");
        }
    }

    class Teacher : Human
    {
        public void Teach()
        {
            Console.WriteLine("I teach programming");
        }
    }
}

显示类型转换

显示类型转换就是在转换对象中构造一个目标类型对象的构造器

namespace ConversionExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Stone stone = new Stone();
            stone.Age = 5000;
            //隐式类型转换  Monkey wukongSun = stone;
            Monkey wukongSun = (Monkey)stone;
            Console.WriteLine(wukongSun.Age);
           
        }
    }

    class Stone
    {
        public int Age;
        //隐式类型转换 public static implicit operator Monkey(Stone stone)
        
        //显示类型转换
        public static explicit operator Monkey(Stone stone)
        {
            Monkey m = new Monkey();
            m.Age = stone.Age/500;
            return m;
        }
    }

    class Monkey 
    {
        public int Age;
    }

}

tips:将explici改成implicit后就能把显式类型转变成隐式类型。

image-20240919173709023

表达式

表达式是一种专门求值的语法实体,可以有一个或多个操作数和零个或多个操作符组成

image-20240919202323138

image-20240919203925716

访问事件 An event access

image-20240919205833804

索引访问表达式

image-20240919210510686

语句

image-20240919211832656

c#语句的定义

image-20240919214540109

参考c#定义文档

虚线以上需要熟练使用

image-20240919221615631

const常量的值是不能够被改变的

字段

image-20240920093245877

C语言参考

image-20240920093739972

代码小记:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataMemberExample
{
     class Program
    {
        static void Main(string[] args)
        {
            List<Student> stuList = new List<Student>();
            for(int i = 0; i < 100; i++)
            {
                Student stu = new Student();
                stu.Age = 24;
                stu.Score = i;
                stuList.Add(stu);

            }

            int totalAge = 0;
            int totalScore = 0; 
            foreach(var stu in stuList)//在一组中一个一个迭代
            {
                totalAge += stu.Age;
                totalScore += stu.Score;
            }

            Student.AverageAge = totalAge / Student.Amount;
            Student.AverageScore = totalScore / Student.Amount; 

            Student.ReportAmount();
            Student.ReportAverageAge();
            Student.ReportAverageScore();
            
        }
    }

    class Student
    {
        public int Age;//实例字段,创建的对象可用的成员变量
        public int Score;
        public readonly int id=100;//声明一个动态只读字段,只有一次机会对其赋值,就是在它的构造器中
        //如果在创建的对象中没有对id进行赋值,那么id的值就是100,否则就是在构造器中赋予的值
        public static readonly Color WColor = new Color() { red = 0, blue = 0, green = 0 };//声明一个静态只读字段


        public static int AverageAge;//静态字段,仅类可用
        public static int AverageScore;
        public  static int Amount;

        public Student()
        {
            Student.Amount++;
        }

        public static void ReportAmount()
        {
            Console.WriteLine(Student.Amount);
        }

        public static void ReportAverageAge()
        {
            Console.WriteLine(Student.AverageAge);
        }

        public static void ReportAverageScore()
        {
            Console.WriteLine(Student.AverageScore);
        }
    }
}

静态只读字段也不能被复制 功能只能被实例保存

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Schema;

namespace DataMemberExample
{
     class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Brush.DefaultColor.Red);
            Console.WriteLine(Brush.DefaultColor.Green);
            Console.WriteLine(Brush.DefaultColor.Blue);
            //静态初始化之后不能被赋值
            //Brush.DefaultColor = new Color() { Red = 0, Green = 0, Blue = 0 };

        }
    }

    struct Color
    {
        public int Red;
        public int Green;
        public int Blue;
    }

    class Brush
    {
        //public static readonly Color DefaultColor = new Color() { Red = 0, Green = 0, Blue = 0 };
        //静态构造函数里静态构造器
        public static readonly Color DefaultColor;
        static Brush()
        {
            Brush.DefaultColor = new Color() { Red = 0, Green = 0, Blue = 0 };
        }



    }
}

实例字段的初始化时机实在创建一个对象的时候进行,静态字段的初始化时机实在运行环境加载该数据类型的时候,并且静态字段的初始化执行一次,即第一次被加载的时候。

set与get方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student();
            stu1.Age = 20;

            Student stu2 = new Student();
            stu2.Age = 20;

            Student stu3 = new Student();
            stu3.Age = 200;

            int avgAge = (stu1.Age + stu2.Age + stu3.Age) / 3;
            Console.WriteLine(avgAge);

        }
    }

    class Student
    {
        public int Age;
    }
}

改进之后的方法 set与get方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace PropertyExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student();
            stu1.SetAge(20);

            Student stu2 = new Student();
            stu2.SetAge(20);

            Student stu3 = new Student();
            stu3.SetAge(80);

            int avgAge = (stu1.GetAge() + stu2.GetAge() + stu3.GetAge()) / 3;
            Console.WriteLine(avgAge);

        }
    }

    class Student
    {
        //public int Age;
        private int age;
        
        public int GetAge()
        {
            return this.age;
        }

        public void SetAge(int value)
        {
            if(value >= 0 && value <= 120)
            {
                this.age = value;
            }
            else
            {
                throw new Exception("Age value has error.");
            }
        }
    }
}

演化

using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace PropertyExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Student stu1 = new Student();
                stu1.Age = 20;

                Student stu2 = new Student();
                stu2.Age = 20;

                Student stu3 = new Student();
                stu3.Age = 20;

                int avgAge = (stu1.Age + stu2.Age + stu3.Age) / 3;
                Console.WriteLine(avgAge);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            

        }
    }

    class Student 
    {
        //public int Age;
        private int age;

        public int Age
        {
            get
            {
                return this.age;
            }

            set
            {
                if (value >= 0 && value <= 120)
                {
                    this.age = value;
                }
                else
                {
                    throw new Exception("Age value has error");
                }
            }
        }
        
        public int GetAge()
        {
            return this.age;
        }

        public void SetAge(int value)
        {
            if(value >= 0 && value <= 120)
            {
                this.age = value;    
            }
            else
            {
                throw new Exception("Age value has error.");
            }
        }
    }
}

属性

什么是属性

image-20240921100947897

属性的声明

静态属性

静态属性(Static Property)是属于类本身的属性,而不是类的实例。可以通过类名直接访问静态属性,而不需要创建类的实例。静态属性在整个应用程序中只有一份副本,可以用于存储类级别的信息。

实例属性

实例属性(Instance Property)是指属于类的实例(对象)的属性。每个类的实例都有自己的一组属性值,这些属性值可以在实例化对象后进行访问和修改。实例属性与静态属性不同,静态属性属于类本身,而实例属性属于类的实例。

只读属性

只读属性(Read-only Property)是指只提供了 Get 访问器的属性,即只能读取属性值,不能修改。只读属性在初始化后不能再被修改,可以用于提供对象的只读视图。

属性也是一种语法

image-20240921101322023

声明属性时 快捷键:propfull 再按两下tab键

 //声明属性时 快捷键:propfull 再按两下tab键
 private int myVar;

 public int MyProperty
 {
     get { return myVar; }
     set { myVar = value; }
 }

静态属性报出异常

using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace PropertyExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Student.Amount = -100;//抛异常
                Console.WriteLine(Student.Amount);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

    class Student 
    {
        //public int Age;
        private int age;

        public int Age//属性(实例属性)
        {
            get//用于返回成员变量的值
            {
                return this.age;
            }

            set//用于设置成员变量的值
            {
                if (value >= 0 && value <= 120)//上下文关键词,value就是传进来的值,不需要声明
                {
                    this.age = value;
                }
                else
                {
                    throw new Exception("Age value has error");
                }
            }
        }

        private static int amount;

        public static int Amount//静态属性
        {
            get { return  amount; }
            set {
                if (value >= 0)
                {
                    Student.amount = value;
                }
                else
                {
                    throw new Exception("Amount has error");
                }
            }
      }

    }
}

结果:
Amount has error
请按任意键继续. . .

动态计算值的属性

主动计算canwork的值
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace PropertyExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Student stu1 = new Student();
                stu1.Age = 16;
                Console.WriteLine(stu1.CanWork);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }
    }

    class Student 
    {

        private int age;
        public int Age
        {
            get { return age; }
            set 
            { age = value;  
            }
        }

        private bool canWork;

        public bool CanWork //动态计算值的属性,并且是主动计算这个值
        {
            get
            {
                if (this.age >= 18)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            
        }
被动
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace PropertyExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Student stu1 = new Student();
                stu1.Age = 12;
                Console.WriteLine(stu1.CanWork);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }
    }

    class Student 
    {

        private int age;
        public int Age
        {
            get { return age; }
            set 
            { age = value;
                //被动引用时
                this.CalcumateCanWork();
            }
        }

        private bool canWork;

        public bool CanWork //动态计算值的属性,并且是主动计算这个值
        {
            get
            {
                return canWork;
            }
            
        }
        

        //以下是被动计算
        private void CalcumateCanWork()
        {
            if (this.age >= 16)
            {
                this.canWork=true;
            }
            else
            {
                this.canWork=false;
            }
        }



    }
}
   

索引器

image-20240921155136588

什么是索引器

在C#中,索引器(Indexer)是一种特殊的属性,允许类的实例像数组一样通过索引来访问和设置对象的元素。通过索引器,可以为类提供类似数组的访问方式,使得对象可以像集合一样进行索引访问。

索引器通常用于实现集合类或类似集合的数据结构,使得可以通过类似数组的语法来访问对象的元素。索引器可以定义多个重载版本,每个版本可以接受不同数量或类型的参数,以便支持不同的索引方式。

相关代码

public class MyCollection
{
    private string[] data = new string[3];
 
    // 索引器的定义
    public string this[int index]
    {
        get { return data[index]; }
        set { data[index] = value; }
    }
}
 
class Program
{
    static void Main()
    {
        MyCollection collection = new MyCollection();
 
        // 设置索引器的值
        collection[0] = "Item 1";
        collection[1] = "Item 2";
        collection[2] = "Item 3";
 
        // 获取索引器的值并输出
        Console.WriteLine(collection[0]);
        Console.WriteLine(collection[1]);
        Console.WriteLine(collection[2]);
    }
}

代码演示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace IndexerExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student();
            stu["Math"] = 90;
            var mathScore = stu["Math"];
            Console.WriteLine(mathScore);
        }
    }


    //索引器声明
    class Student
    {
        private Dictionary<string, int> scoreDictionary = new Dictionary<string, int>();

        public int? this[String subject]
        {
            get
            {
                if (this.scoreDictionary.ContainsKey(subject))
                {
                    return this.scoreDictionary[subject];
                }
                else
                {
                    return null;
                }
            }
            set
            {
                if(value.HasValue == false)
                {
                    throw new Exception("Score cannot be null.");
                }

                if (this.scoreDictionary.ContainsKey(subject))
                {
                    this.scoreDictionary[subject] = value.Value;
                }
                else
                {
                    this.scoreDictionary.Add(subject, value.Value);
                }
            }
        }


    }
}

常量

image-20240921163910628

参数

传值参数

image-20240921173311574

传值参数(值类型)

声明时不带修饰符的形参是值形参。一个值形参对应于一个局部变量,只是它的初始值来自该方法调用所提供的相应实参。

当形参是值形参时,方法调用中的对应实参必须是表达式,并且它的类型可以隐式转换(第 ‎6.1 节)为形参的类型。

允许方法将新值赋给值参数。这样的赋值只影响由该值形参表示的局部存储位置,而不会影响在方法调用时由调用方给出的实参。

代码演示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ParametersExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //实例方法,先创建方法的实例
            Student stu = new Student();
            int y = 100;
            stu.AddOne(y);
            Console.WriteLine(y);
        }
    }

    //方法声明
    class Student
    {
        public void AddOne(int x)//x参数是传值参数,传进来的值会在方法体内部有一个副本,改变的是副本的值,并不会影响方法体外的值 
        {
            x = x + 1;
            Console.WriteLine(x);
        }
    }
}

传值参数(引用类型)

创建新对象

image-20240921190537633

不创建对象

image-20240921215912954

代码演示:

创建对象

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ParametersExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student() { Name = "Tim" };
            SomeMethod(stu);//打印出来是Tom  因为参数传进来时,SomeMethod方法给Name赋了一个新值,所以打印出来的是Tom
            Console.WriteLine(stu.Name);//这个是方法外部变量所引用的实例没有变

        }

        //生					

推荐阅读