• 【C#进阶】C#语法中一些常用知识点总结


    很久没有用写C#代码了,最近花了点时间重温了一下C#语法的一些知识点,查缺补漏!

    1.三目运算符

    //三目运算符:?:
    //如 a>0?1:2
    int a = -1;
    // 通过?以及:将表达式分为3部分
    var result = a > 0 ? 1 : 2;
    // ?号前表示条件,得到一个bool值,如果为真,返回:号前面的结果,否则返回:号后面的结果 
    Console.WriteLine(result);
    
    // 当a>0  返回1
    // 当a<0  返回2
    // 否则   返回0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    代码示例:

    a = 1;
    result = a > 0 ? 1 : (a < 0 ? 2 : 0);//1
    Console.WriteLine(result);
    a = -1;
    result = a > 0 ? 1 : (a < 0 ? 2 : 0);//2
    Console.WriteLine(result);
    a = 0;
    result = a > 0 ? 1 : (a < 0 ? 2 : 0);//0
    Console.WriteLine(result);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.循环控制语句 (for while do…while foreach)

    // 针对数据集合进行遍历
    int[] values = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    Console.WriteLine(values);
    for (int i = 0; i < values.Length; i++)
    {
        // i:0 1 2 3 4 5 6 7 8    
        //   0   2   4   6   8
        if (i % 2 == 0)
            Console.WriteLine(values[i]);
    
        if (i % 2 > 0) continue;
        Console.WriteLine(values[i]);
    
        // break : 跳转循环,循环终止
        // continue : 结当次循环(后面的代码不执行了),直接执行下次循环
    }
    
    int index = 0;
    // foreach:遍历每一个元素
    foreach (int i in values)
    {
        Console.WriteLine(i);
        index++;
    }
    
    // while :首先判断条件,条件满足进入循环。可能一次都执行不了
    index = 5;
    while (index < 5)
    {
        // 死循环 index < 5一直为True
        Console.WriteLine("Hello");
        index++;
    }
    Console.WriteLine("while结束");
    
    // do..while   选择执一遍,再判断条件,条件满足,继续执行,不满足即跳出。至少可以执一次
    index = 5;
    do
    {
        Console.WriteLine("Hello");
        index++;
    } while (index < 5);
    
    • 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
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    3.访问修饰符

    你可以把访问修饰符想象成一个家庭的不同房间和钥匙。public就像是客厅,任何人都可以进入。private是你的私人房间,只有你自己可以进入。protected像是家庭成员共用的房间,只有家庭成员(派生类)可以进入。internal则是整个家(同一程序集)的公共空间,只要是住在这个家里的人都可以进入。protected internalprivate protected则是钥匙的组合,给予更多灵活的访问控制。

    1. public:完全公开的。同一程序集中的任何其他代码或引用该程序集的其他程序集都可以访问该类型或成员。 某一类型的公共成员的可访问性水平由该类型本身的可访问性级别控制。(访问级别 = 0)
    2. private:只有同一 class 或 struct 中的代码可以访问该类型或成员。(访问级别 = 5)
    3. protected:只有同一 class 或者从该 class 派生的 class 中的代码可以访问该类型或成员。(访问级别 = 4)
    4. internal:同一程序集中的任何代码都可以访问该类型或成员,但其他程序集中的代码不可以。(访问级别 = 3)
    5. protected internal:该类型或成员可由对其进行声明的程序集或另一程序集中的派生 class 中的任何代码访问。(访问级别 = 2)
    6. private protected:该类型或成员可以通过从 class 派生的类型访问,这些类型在其包含程序集中进行声明。(访问级别 = 1)

    4.静态方法和非静态方法

    假设一个类是一个“房子”的蓝图。静态方法就像是这个蓝图上标注的固定信息,比如房屋的总平方尺数——你不需要建造一个实际的房子(实例化一个对象)来获取这个信息。非静态方法则像是房子里的开关或电器,你需要进入一个实际的房子(创建一个对象)来操作它们。

    静态方法: 静态方法属于类本身,而不是类的实例。因此,无需创建类的实例就可以调用静态方法, 使用static关键字进行定义。

    • 访问限制:静态方法不能访问类的非静态成员。
    • 状态保持:静态方法不能使用或更改非静态字段,因为它们不依赖于对象状态。
    • 扩展性:不能被继承或重写。
    • 用途:通常用于实现不依赖于对象状态的功能,例如工具函数。

    非静态方法: 非静态方法属于类的实例。需要先创建类的对象,然后才能调用这些方法。通常不需要特殊关键字,除非用于继承或重写等特殊情况。

    • 访问限制:非静态方法可以访问类中的所有成员。
    • 状态保持:可以使用或更改对象的状态(字段)。
    • 扩展性:可以被继承和重写。
    • 用途:通常用于实现依赖于或更改对象状态的功能。

    5.数组、字典和其他集合类型

    • 数组:就像一列有固定数量的停车位,每个停车位只能停一辆特定类型的车。
    • 列表:更像一个动态的停车场,可以根据需要增加或减少停车位。
    • 字典:就像一个大楼的邮箱,每个邮箱有唯一的标签和里面的邮件。
    • 队列:就像排队等候,先来的先得到服务。
    • :就像一叠盘子,最后放上去的盘子会先被取下。
    • 散列集:就像一个房间里的人,每个人都是唯一的,但你不能预测他们站在房间里的哪个位置。

    1. 数组(Array)

    • 定义方式: 使用方括号 []

    • 特性: 长度固定,元素类型相同。

    • 访问: 使用索引,从 0 开始。

    • 示例:

      int[] numbers = new int[5] {1, 2, 3, 4, 5};
      
      • 1
    • 常用操作: Length 获取长度, SetValueGetValue 设置和获取值。

    2. 列表(List)

    • 定义方式: 使用 List 类。

    • 特性: 动态大小,元素类型相同。

    • 访问: 使用索引,从 0 开始。

    • 示例:

      List numbers = new List {1, 2, 3, 4, 5};
      
      • 1
    • 常用操作: Add, Remove, Count, Contains

    3. 字典(Dictionary)

    • 定义方式: 使用 Dictionary 类。

    • 特性: 键值对存储,键唯一。

    • 访问: 使用键。

    • 示例:

      Dictionary age = new Dictionary
      {
        {"Alice", 30},
        {"Bob", 40}
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • 常用操作: Add, Remove, ContainsKey, TryGetValue

    4. 队列(Queue)

    • 定义方式: 使用 Queue 类。

    • 特性: 先进先出(FIFO)。

    • 访问: 不能使用索引。

    • 示例:

      Queue numbers = new Queue();
      
      • 1
    • 常用操作: Enqueue, Dequeue, Peek, Count

    5. 栈(Stack)

    • 定义方式: 使用 Stack 类。

    • 特性: 后进先出(LIFO)。

    • 访问: 不能使用索引。

    • 示例:

      Stack numbers = new Stack();
      
      • 1
    • 常用操作: Push, Pop, Peek, Count

    6. 散列集(HashSet)

    • 定义方式: 使用 HashSet 类。

    • 特性: 元素唯一,无序。

    • 访问: 不能使用索引。

    • 示例:

      HashSet numbers = new HashSet {1, 2, 3};
      
      • 1
    • 常用操作: Add, Remove, Contains, Count

    总结一下

    类型长度是否固定元素是否唯一是否有序可通过索引访问使用场景
    数组固定大小
    列表动态内容
    字典键是否(通过键访问)配置设置
    队列是(FIFO)打印队列、等待列表
    是(LIFO)撤销操作、深度优先搜索
    散列集停止词、唯一标识符集

    6. out和ref

    假设你要从一个魔法店里取出一些物品。

    • 使用 out 就像是你给店主一个空袋子,店主一定会在里面放一些东西。
    • 使用 ref 就像是你给店主一个已经有东西的袋子,店主可以查看里面的东西,也可以添加或更改里面的东西。

    out 参数用于从方法返回多个值。使用 out 参数时:

    1. 在方法内必须为 out 参数赋值。
    2. 调用方法时,传入的变量不需要预先赋值。
    public void GetData(out int x, out string y)
    {
        x = 10;
        y = "hello";
    }
    
    // 调用
    int a;
    string b;
    GetData(out a, out b); //此时a=10,b="hello"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ref 参数用于双向传递。即:

    1. 在方法内可以访问 ref 参数的初始值。
    2. 在方法内可以更改 ref 参数的值,该更改将反映到外部变量。
    3. 调用方法时,传入的变量必须预先赋值。
    public void ModifyData(ref int x)
    {
        x = x * 2;
    }
    
    // 调用
    int a = 5;
    ModifyData(ref a);  // a 现在是 10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    7.属性和变量

    变量(也称为字段)是类、结构或枚举中定义的成员,用于存储数据。它们可以有访问修饰符(如 public, private 等)。

    public class MyClass
    {
        public int MyField;  // 公有字段
        private string anotherField;  // 私有字段
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    属性提供了一种封装字段的方式,允许你通过 getset 访问器来控制字段的读取和写入。这有助于实现更好的数据封装和验证逻辑。

    public class MyClass
    {
         public int MyProperty_0 { get; set; }
         public int MyProperty_1 { get; }// 只有get没有set,表示这个属性是只读
         public int MyProperty_2 { get; private set; } //外部不可修改
         //public int MyProperty_2 { set; }// 不允许只写不读
        
        //更灵活的写法
        private int _myField;  // 私有字段
        public int MyProperty  // 公有属性
        {
            get { return _myField; }
            set
            {
                if (value >= 0) //进行更多操作
                    _myField = value;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    8.构造函数

    构造函数就像是产品(对象)出厂时的“初始化设置”过程。在这个过程中,工厂(构造函数)会根据需求(参数)来设置产品(对象)的各种特性和功能(字段和属性)。

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    
        public Person(string name, int age) //构造函数
        {
            this.Name = name;
            this.Age = age;
        }
    }
    // 使用
    Person person = new Person("Alice", 30);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    一个类可以拥有多个构造函数,这被称为构造函数重载

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public string Email { get; set; }
    
        // 无参数的构造函数
        public Person()
        {
            Name = "Unknown";
            Age = 0;
            Email = "unknown@example.com";
        }
    
        // 接受一个参数的构造函数
        public Person(string name)
        {
            Name = name;
            Age = 0;
            Email = "unknown@example.com";
        }
    
        // 接受两个参数的构造函数
        public Person(string name, int age)
        {
            Name = name;
            Age = age;
            Email = "unknown@example.com";
        }
    
        // 接受三个参数的构造函数
        public Person(string name, int age, string email)
        {
            Name = name;
            Age = age;
            Email = email;
        }
    }
    
    // 使用无参数构造函数
    Person person1 = new Person();
    
    // 使用一个参数的构造函数
    Person person2 = new Person("Alice");
    
    // 使用两个参数的构造函数
    Person person3 = new Person("Bob", 30);
    
    // 使用三个参数的构造函数
    Person person4 = new Person("Charlie", 40, "charlie@example.com");
    
    • 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
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    9.面向对象(封装、继承、多态)

    1. 封装(Encapsulation)

      • 封装是将数据(字段)和相关操作(方法)封装在一个类中的概念。这可以通过访问修饰符(如publicprivateprotected)来实现,以控制数据的访问级别,防止直接访问内部数据。

      • 这有点像手机的外壳,你可以使用屏幕、按钮等接口来与手机交互,但不能直接触及手机内部的电路。

      • 例子:

        public class Person
        {
            private string name;
            private int age;
        
            public string GetName()
            {
                return name;
            }
        
            public void SetName(string newName)
            {
                name = newName;
            }
        
            public int GetAge()
            {
                return age;
            }
        
            public void SetAge(int newAge)
            {
                if (newAge >= 0)
                {
                    age = newAge;
                }
            }
        }
        //name和age字段被封装在Person类中,并通过公有方法GetName()、SetName()、GetAge()和SetAge()来访问和修改它们,防止直接访问字段。
        
        • 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
        • 27
        • 28
        • 29
    2. 继承(Inheritance)

      • 允许一个类(子类)继承另一个类(父类)的成员(字段和方法),并且可以在子类中添加新成员或重写父类的成员。
      • 想象一下家庭中的关系,孩子可以继承父母的一些遗传特征,如眼睛颜色,同时也可以拥有自己独特的特征。
      • 例子:
      public class Vehicle
      {
          public string Make { get; set; }
          public string Model { get; set; }
      
          public void StartEngine()
          {
              Console.WriteLine("Engine started.");
          }
      }
      
      public class Car : Vehicle
      {
          public int Year { get; set; }
      
          public void Accelerate()
          {
              Console.WriteLine("Car is accelerating.");
          }
      }
      //在这个例子中,Car类继承了Vehicle类的Make、Model属性和StartEngine()方法,并且添加了自己的属性Year和方法Accelerate()。
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
    3. 多态(Polymorphism)

      • 允许不同的对象对相同的方法做出不同的响应。这可以通过方法重写和接口来实现。
      • 这就像电器插座适配器,不同的电器可以插入同一个插座,但它们在插座中的表现不同。
      • 例子:
      public class Animal
      {
          public virtual void MakeSound()
          {
              Console.WriteLine("Some generic animal sound.");
          }
      }
      
      public class Dog : Animal
      {
          public override void MakeSound()
          {
              Console.WriteLine("Woof! Woof!");
          }
      }
      
      public class Cat : Animal
      {
          public override void MakeSound()
          {
              Console.WriteLine("Meow!");
          }
      }
      //Animal类有一个虚拟方法MakeSound(),Dog和Cat类分别重写了这个方法,使得它们可以表现出不同的声音。其中还使用virtual 和 override 关键字是一种明确的方式来声明和表达方法的重写关系,有助于代码的可读性和维护性。
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24

    10.接口(Interface)

    1. 定义方式:使用interface关键字。
    2. 成员:只能包含方法、属性、事件和索引器的声明,不能包含实现。
    3. 访问修饰符:所有成员自动为public,不允许添加其他访问修饰符。
    4. 多继承:一个类可以实现多个接口。
    5. 实现:实现接口的类必须提供接口中所有成员的实现。

    示例:

    1.定义接口

    //假设正在开发一个绘图程序,需要支持多种图形(如圆形、矩形等)。可以定义一个IDrawable接口:
    interface IDrawable
    {
        void Draw();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.实现接口定义类

    //定义不同的图形类,如Circle和Rectangle,并实现IDrawable接口:
    class Circle : IDrawable
    {
        public void Draw()
        {
            Console.WriteLine("Drawing a circle.");
        }
    }
    
    class Rectangle : IDrawable
    {
        public void Draw()
        {
            Console.WriteLine("Drawing a rectangle.");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3.使用

    IDrawable[] drawables = new IDrawable[] { new Circle(), new Rectangle() };
    foreach (var drawable in drawables)
    {
        drawable.Draw();
    }
    //Drawing a circle.
    //Drawing a rectangle.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    11.抽象类

    1. 定义方式:使用abstract关键字。
    2. 成员:可以包含字段、方法、属性、事件和索引器。方法可以有或没有实现。
    3. 访问修饰符:成员可以有不同的访问修饰符(publicprotected等)。
    4. 多继承:一个类只能继承一个抽象类。
    5. 实现:继承抽象类的子类必须实现所有抽象成员。

    示例:

    1.定义抽象类

    public abstract class Shape
    {
        public abstract void Draw();  // 抽象方法
    
        public void Move()  // 具体方法
        {
            Console.WriteLine("Moving the shape.");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.继承抽象类

    //定义Circle和Rectangle类,并继承Shape:
    class Circle : Shape
    {
        public override void Draw()
        {
            Console.WriteLine("Drawing a circle.");
        }
    }
    
    class Rectangle : Shape
    {
        public override void Draw()
        {
            Console.WriteLine("Drawing a rectangle.");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3.使用

    Shape[] shapes = new Shape[] { new Circle(), new Rectangle() };
    foreach (var shape in shapes)
    {
        shape.Draw();
        shape.Move();
    }
    //Drawing a circle.
    //Moving the shape.
    //Drawing a rectangle.
    //Moving the shape.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    精彩推荐:
    【C#进阶一】C#中的数组(Array)、集合(ArrayList,Queue,Stack, HashList)、List<T>、字典(Dictionary<K,T>)和双向链表LinkedList
    【C#进阶八】C#中的序列化与反序列化下(二进制序列化、XML序列化及JSON序列化)

    希望有所帮助,同时欢迎关注我,后面将更新更多相关内容!

  • 相关阅读:
    【MQ简单模式】
    Jmeter常用参数化技巧总结!
    项目中使用 husky 格式化代码和校验 commit 信息
    第2章搭建CRM项目开发环境(搭建开发环境)
    Java版分布式微服务云开发架构 Spring Cloud+Spring Boot+Mybatis 电子招标采购系统功能清单
    一文搞懂MySQL事务的隔离性如何实现|MVCC
    web3相关教程资讯集锦
    Jenkins使用pipeline部署服务到远程服务器
    阿里云ECS服务器如何搭建并连接FTP,完整步骤
    多仓库管理工具git-repo部署
  • 原文地址:https://blog.csdn.net/QH2107/article/details/133977594