C#理论 —— 基础语法、数据类型、变量、常量、运算符、三大结构

news2024/11/18 21:37:02

文章目录

  • 1. 基础语法
    • 1.1 标识符命名规则
    • 1.2 C# 关键字
    • 1.3 C#注释
  • 2. 数据类型
    • 2.1 值类型(Value types)
    • 2.2 引用类型(Reference types)
      • 2.2.1 对象(Object)类型
      • 3.2.2 动态(Dynamic)类型
      • 2.2.3 字符串(String)类型
        • 2.2.3.1 关于@
    • 2.3 指针类型(Pointer types)
    • 2.4 数据类型转换
      • 2.4.1 内置的类型转换方法
    • 2.5 可空类型(Nullable)
      • 2.5.1 单问号 ? 与 双问号 ??
    • 2.6 数组(Array)
      • 2.6.1 声明/初始化/赋值数组
      • 2.6.2 访问数组元素
      • 2.6.3 使用 foreach 循环遍历数组
      • 2.6.4 多维数组
    • 2.7 字符串(String)
      • 2.7.1 创建 String 对象
      • 2.7.2 String 类的属性
      • 2.7.3 String 类的方法
      • 2.7.* 例程
        • 2.7.*.1 比较字符串
        • 2.7.*.2 查找字符串中指定的字符串
        • 2.7.*.3 获取子字符串(切片)
        • 2.7.*.4 拼接字符串
    • 2.8 结构体(Struct)
      • 2.8.1 定义结构体/结构体变量
      • 2.8.2 结构体与类的区别
    • 2.9 枚举(Enum)
    • 2.10 数据类型转换
      • string 相关的转化
  • 3. 变量
    • 3.1 变量类型转换
    • 3.2 强制类型转换
    • 3.3 变量作用域
    • 3.4 变量修饰符
      • 3.4.1 `out`修饰符展示
      • 3.4.2 `ref`修饰符展示
  • 4. 常量
    • 4.1 整型常量
    • 4.2 浮点型常量
    • 4.3 字符型常量
    • 4.4 字符串型常量
    • 4.5 常量的定义
  • 5. 运算符
    • 5.1 算术运算符
      • 5.1.1 关于i++与++i
    • 5.2 关系运算符
    • 5.3 逻辑运算符
    • 5.4 位运算符
    • 5.5 赋值运算符
    • 5.6 其他运算符
    • 5.7 运算符优先级
  • 6. 三大结构
    • 6.1 条件结构
      • 6.1.1 switch语句
      • 6.1.2 条件运算符 ?:
    • 6.2 循环结构
      • 6.2.1 for/foreach 循环
      • 6.2.2 循环控制语句

1. 基础语法


1.1 标识符命名规则

标识符是用来识别类、变量、函数或任何其它用户定义的项目(即变量名、函数名、类名等)。在 C# 中,类的命名必须遵循如下基本规则:

  • 必须以字母、下划线或 @ 开头。第一个字符不能是数字。
  • 必须不包含任何嵌入的空格或符号,比如 ? - +! # % ^ & * ( ) [ ] { } . ; : " ’ / \。
  • 不能是 C# 关键字。除非它们有一个 @ 前缀。 例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字。
  • 必须区分大小写。大写字母和小写字母被认为是不同的字母。
  • 不能与C#的类库名称相同。

1.2 C# 关键字

关键字是 C# 编译器预定义的保留字。这些关键字不能用作标识符。有些关键字在代码的上下文中有特殊的意义,如 getset,这些被称为上下文关键字(Contextual keywords)。

  • C#关键字

1.3 C#注释

文档注释只需在方法或类定义的上一行输入///即可自动生成注释模板。一般文档注释也只用在方法或类上。

// 单行注释

/*
多
行
注
释
*/

///<summary>
///文档注释
///</summary>

2. 数据类型


在 C# 中,变量分为以下几种类型:

  • 值类型(Value types)
  • 引用类型(Reference types)
  • 指针类型(Pointer types)

2.1 值类型(Value types)

值类型数据可以直接分配给其一个值。它们是从类 System.ValueType 中派生的。
被赋值后的值类型变量就包含了数据。比如int、char、float等数据类型,它们分别存储数字、字符、浮点数。当您声明一个 int 类型的变量时,系统分配内存来存储该变量的值。

  • 值类型

2.2 引用类型(Reference types)

引用类型不包含存储在变量中的实际数据,但包含对变量的引用。即它们指向的是一个内存位置。内置的引用类型有:objectdynamicstring

  • 应用类型

2.2.1 对象(Object)类型

该类型是C# 通用类型系统(Common Type System - CTS)中所有数据类型的最终基类。Object 是 System.Object 类的别名。所以对象(Object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。

当一个值类型转换为对象类型时,则被称为装箱;反之,当一个对象类型转换为值类型时,则被称为拆箱

object obj; // 声明对象类型变量
obj = 100; // 装箱

3.2.2 动态(Dynamic)类型

该类型可存储任何类型的值在动态数据类型变量中。这些变量的类型定义在运行时发生。

动态类型与对象类型相似,但对象类型变量的类型定义是在编译时发生的,而动态类型变量的类型定义是在运行时发生的。

dynamic d = 20; // 声明并初始化动态类型变量

2.2.3 字符串(String)类型

该类型允许给变量分配任何字符串值。字符串(String)类型是System.String 类的别名。它从对象(Object)类型派生的。字符串(String)类型的值可以通过两种形式进行分配:引号""和 @引号@""

String str = "run"; // 声明并初始化字符串类型变量
@"runoob.com"; // @引号字符串
2.2.3.1 关于@

@逐字字符串,其作用如下:

  • 可将转义字符\当作普通字符对待。
  • @ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。
string str = @"C:\Windows";
等价于:
string str = "C:\\Windows";

string str = @"<script type=""text/javascript"">
    <!--
    -->
</script>";

2.3 指针类型(Pointer types)

指针类型存储另一种类型的内存地址。C# 中的指针与 C 或 C++ 中的指针有相同的功能。

char* cptr; //声明指针变量
int* iptr;

2.4 数据类型转换

C# 中,数据类型转换有两种形式:

  • 隐式类型转换:C# 默认的自动的以安全方式进行的转换, 它不会导致数据的丢失。例如,从小的整数类型转换为大的整数类型,从派生类转换为基类。
  • 显式类型转换:即强制类型转换。显式转换需要强制转换运算符,强制转换会造成数据的丢失。
using System;
// using System.Collections.Generic;
// using System.Linq;
// using System.Text;
// using System.Threading.Tasks;

namespace TypeConversionApplication
{
    class ExplicitConversion
    {
        static void Main(string[] args)
        {
            double d = 5673.74;
            int i;

            
            i = (int)d; // 强制转换 double 为 int类型
            Console.WriteLine(i);
            Console.ReadKey();

        }
    }
}

控制台输出:5673

2.4.1 内置的类型转换方法

方法命功能描述
ToBoolean如果可能的话,把类型转换为布尔型。
ToByte把类型转换为字节类型。
ToChar如果可能的话,把类型转换为单个 Unicode 字符类型。
ToDateTime把类型(整数或字符串类型)转换为 日期-时间 结构。
ToDecimal把浮点型或整数类型转换为十进制类型。
ToDouble把类型转换为双精度浮点型。
ToInt16把类型转换为 16 位整数类型。
ToInt32把类型转换为 32 位整数类型。
ToInt64把类型转换为 64 位整数类型。
ToSbyte把类型转换为有符号字节类型。
ToSingle把类型转换为小浮点数类型。
ToString把类型转换为字符串类型。
ToType把类型转换为指定类型。
ToUInt16把类型转换为 16 位无符号整数类型。
ToUInt32把类型转换为 32 位无符号整数类型。
ToUInt64把类型转换为 64 位无符号整数类型。
using System;
// using System.Collections.Generic;
// using System.Linq;
// using System.Text;
// using System.Threading.Tasks;

namespace TypeConversionApplication
{
    class StringConversion
    {
        static void Main(string[] args)
        {
            int i = 75;
            float f = 53.005f;
            double d = 2345.7652;
            bool b = true;

            Console.WriteLine(i.ToString());
            Console.WriteLine(f.ToString());
            Console.WriteLine(d.ToString());
            Console.WriteLine(b.ToString());
            Console.ReadKey();

        }
    }
}

2.5 可空类型(Nullable)

C# 提供了一个特殊的数据类型,nullable 类型(可空类型),被定义为可空类型的变量允许表示其基础值类型正常范围内的所有值或 null 值。

例如,Nullable< Int32 >,读作"可空的 Int32",它可被赋值为 -2,147,483,648 到 2,147,483,647 之间的任意值,也可被赋值为 null 值。类似的,Nullable< bool > 变量可以被赋值为 true 或 false 或 null。

在处理数据库和其他包含可能未赋值的元素的数据类型时,将 null 赋值给数值类型或布尔型的功能特别有用。

例如,数据库中的布尔型字段可以存储值 true 或 false,或该字段也可以未定义,即null。

using System;
namespace CalculatorApplication
{
   class NullablesAtShow
   {
      static void Main(string[] args)
      {
         int? num1 = null; // 定义可空的整数型变量
         int? num2 = 45;
         double? num3 = new double?(); // 定义可空的双精度浮点型变量
         double? num4 = 3.14157;
         
         bool? boolval = new bool?(); // 定义可空的布尔型变量

         // 显示值
         
         Console.WriteLine("显示可空类型的值: {0}, {1}, {2}, {3}", 
                            num1, num2, num3, num4);
         Console.WriteLine("一个可空的布尔值: {0}", boolval);
         Console.ReadLine();

      }
   }
}

2.5.1 单问号 ? 与 双问号 ??

单问号?用于对 int,double,bool 等无法直接赋值为 null 的数据类型进行 null 的赋值,即这个数据类型是 Nullable 类型的。

int i; //默认值为0
int? ii; //默认值为null

双问号??又叫合并运算符,可用于判断一个变量是否为 null ,是则返回一个指定的值。

double? num1 = null;
double? num2 = 3.14157;
double num3;
num3 = num1 ?? 5.34;      // num1 如果为空值则返回 5.34,否则返回num1的值
Console.WriteLine("num3 的值: {0}", num3);
num3 = num2 ?? 5.34; // num2 如果为空值则返回 5.34,否则返回num2的值,即3.14157
Console.WriteLine("num3 的值: {0}", num3);

运行结果:
num3 的值: 5.34
num3 的值: 3.14157

2.6 数组(Array)

数组是一个存储相同类型元素的固定大小的有序的集合。

数组是由连续的内存位置组成的。最低的地址对应第一个元素,最高的地址对应最后一个元素。

2.6.1 声明/初始化/赋值数组

数组是一个引用类型,需要使用 new 关键字来创建数组的实例。

声明数组后,内存中不会为其分配内粗。只有当数组被初始化后,才会为其分配内存,此后才能给数组赋值。也可以在声明数组的同时,对数组进行赋值(相当于初始化与赋值同步进行)。

数组之间赋值时,目标和源都会指向相同的内存位置。

double[] balance; // 声明数组
double[] balance = new double[10]; // 声明并初始化数组
balance[0] = 4500.0; // 数组赋值

double[] balance = {2340.0, 4523.69, 3421.0}; // 声明数组的同时赋值数组
int [] marks = new int[5]  { 99,  98, 92, 97, 95}; // 同时声明、初始化、赋值数组。其中数组的大小可以省略。
int[] score = marks; // 赋值一个数组变量到另一个数组

2.6.2 访问数组元素

元素是通过带索引的数组名称来访问的。

double salary = balance[9];

2.6.3 使用 foreach 循环遍历数组

using System;
// using System.Collections.Generic;
// using System.Linq;
// using System.Text;
// using System.Threading.Tasks;
namespace ArrayApplication
{
    class MyArray
    {
        static void Main(string[] args)
        {
            int[] n = new int[10]; /* n 是一个带有 10 个整数的数组 */


            /* 初始化数组 n 中的元素 */
            for (int i = 0; i < 10; i++)
            {
                n[i] = i + 100;
            }

            /* 输出每个数组元素的值 */
            foreach (int j in n)
            {
                int i = j - 100;
                Console.WriteLine("Element[{0}] = {1}", i, j);
            }
            Console.ReadKey();
        }
    }
}

2.6.4 多维数组

多维数组又称为矩形数组。一个二维数组,在本质上,是一个一维数组的列表。

string [,] names; // 声明一个二维数组
int [ , , ] m; // 声明一个三维数组
  • 初始化二维数组:多维数组可以通过在括号内为每行指定值来进行初始化。
int [,] a = new int [3,4] {
 {0, 1, 2, 3} ,   /*  初始化索引号为 0 的行 */
 {4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
 {8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
};
  • 访问二维数组元素
int val = a[2,3];

2.7 字符串(String)

在 C# 中,可以像C语言一样使用字符数组来表示字符串,但更常见的做法是使用 string 关键字来声明一个字符串变量。string 关键字是 System.String 类的别名。

2.7.1 创建 String 对象

可以使用以下方法之一来创建 string 对象:

  • 通过给 String 变量赋值一个字符串
  • 通过使用 String 类构造函数
  • 通过使用字符串串联运算符( + )
  • 通过检索属性或调用一个返回字符串的方法
  • 通过格式化方法来转换一个值或对象为它的字符串表示形式
using System;

namespace StringApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            //字符串,字符串连接
            string fname, lname; // 定义string变量
            fname = "an"; 
            lname = "tennin";

            string fullname = fname + lname;
            Console.WriteLine("Full Name: {0}", fullname);

            //通过使用 string 构造函数
            char[] letters = { 'H', 'e', 'l', 'l', 'o' }; // 定义并初始化字符数组
            string greetings = new string(letters); 
            Console.WriteLine("Greetings: {0}", greetings);

            //方法返回字符串
            string[] sarray = { "Hello", "From", "antennin", "Point" }; // 定义并初始化字符串数组
            string message = String.Join(" ", sarray); // 使用Join方法插入字符串
            Console.WriteLine("Message: {0}", message);

            //用于转化值的格式化方法
            DateTime waiting = new DateTime(2012, 10, 10, 17, 58, 1);
            string chat = String.Format("Message sent at {0:t} on {0:D}",waiting);
            Console.WriteLine("Message: {0}", chat);
            Console.ReadKey();
        }
    }
}

运行输出:
Full Name: antennin
Greetings: Hello
Message: Hello From antennin Point
Message: Message sent at 17:58 on 20121010

2.7.2 String 类的属性

String 类有两个属性:

属性名称描述
Chars在当前 String 对象中获取 Char 对象的指定位置。
Length在当前的 String 对象中获取字符数。

2.7.3 String 类的方法

下面的提供了一些最常用的string方法:

转到函数的定义处即可查看到该函数的重载和官方应用说明。

方法名称描述
public static int Compare( string strA, string strB )比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。该方法区分大小写。
public static int Compare( string strA, string strB, bool ignoreCase )比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。但是,如果布尔参数为真时,该方法不区分大小写。
public static string Concat( string str0, string str1 )连接两个 string 对象。
public static string Concat( string str0, string str1, string str2 )连接三个 string 对象。
public static string Concat( string str0, string str1, string str2, string str3 )连接四个 string 对象。
public bool Contains( string value )返回一个表示指定 string 对象是否出现在字符串中的值。
public static string Copy( string str )创建一个与指定字符串具有相同值的新的 String 对象。
public void CopyTo( int sourceIndex, char[] destination, int destinationIndex, int count )从 string 对象的指定位置开始复制指定数量的字符到 Unicode 字符数组中的指定位置。
public bool EndsWith( string value )判断 string 对象的结尾是否匹配指定的字符串。
public bool Equals( string value )判断当前的 string 对象是否与指定的 string 对象具有相同的值。
public static bool Equals( string a, string b )判断两个指定的 string 对象是否具有相同的值。
public static string Format( string format, Object arg0 )把指定字符串中一个或多个格式项替换为指定对象的字符串表示形式。
public int IndexOf( char value )返回指定 Unicode 字符在当前字符串中第一次出现的索引,索引从 0 开始。
public int IndexOf( string value )返回指定字符串在该实例中第一次出现的索引,索引从 0 开始。
public int IndexOf( char value, int startIndex )返回指定 Unicode 字符从该字符串中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。
public int IndexOf( string value, int startIndex )返回指定字符串从该实例中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。
public int IndexOfAny( char[] anyOf )返回某一个指定的 Unicode 字符数组中任意字符在该实例中第一次出现的索引,索引从 0 开始。
public int IndexOfAny( char[] anyOf, int startIndex )返回某一个指定的 Unicode 字符数组中任意字符从该实例中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。
public string Insert( int startIndex, string value )返回一个新的字符串,其中,指定的字符串被插入在当前 string 对象的指定索引位置。
public static bool IsNullOrEmpty( string value )指示指定的字符串是否为 null 或者是否为一个空的字符串。
public static string Join( string separator, string[] value )连接一个字符串数组中的所有元素,使用指定的分隔符分隔每个元素。
public static string Join( string separator, string[] value, int startIndex, int count )连接接一个字符串数组中的指定位置开始的指定元素,使用指定的分隔符分隔每个元素。
public int LastIndexOf( char value )返回指定 Unicode 字符在当前 string 对象中最后一次出现的索引位置,索引从 0 开始。
public int LastIndexOf( string value )返回指定字符串在当前 string 对象中最后一次出现的索引位置,索引从 0 开始。
public string Remove( int startIndex )移除当前实例中的所有字符,从指定位置开始,一直到最后一个位置为止,并返回字符串。
public string Remove( int startIndex, int count )从当前字符串的指定位置开始移除指定数量的字符,并返回字符串。
public string Replace( char oldChar, char newChar )把当前 string 对象中,所有指定的 Unicode 字符替换为另一个指定的 Unicode 字符,并返回新的字符串。
public string Replace( string oldValue, string newValue )把当前 string 对象中,所有指定的字符串替换为另一个指定的字符串,并返回新的字符串。
public String[] Split(String[] separator, StringSplitOptions options);基于数组separator中的字符串将字符串拆分为多个子字符串。
public String[] Split(String[] separator, int count, StringSplitOptions options);基于数组separator中的字符将一个字符串拆分成最大数量的子字符串。
public bool StartsWith( string value )判断字符串实例的开头是否匹配指定的字符串。
public char[] ToCharArray()返回一个带有当前 string 对象中所有字符的 Unicode 字符数组。
public char[] ToCharArray( int startIndex, int length )返回一个带有当前 string 对象中所有字符的 Unicode 字符数组,从指定的索引开始,直到指定的长度为止。
public string ToLower()把字符串转换为小写并返回。
public string ToUpper()把字符串转换为大写并返回。
public string Trim()移除当前 String 对象中的所有前导空白字符和后置空白字符。

2.7.* 例程

2.7.*.1 比较字符串
using System;

namespace StringApplication
{
   class StringProg
   {
      static void Main(string[] args)
      {
         string str1 = "This is test";
         string str2 = "This is text";

         if (String.Compare(str1, str2) == 0)
         {
            Console.WriteLine(str1 + " and " + str2 +  " are equal.");
         }
         else
         {
            Console.WriteLine(str1 + " and " + str2 + " are not equal.");
         }
         Console.ReadKey() ;
      }
   }
}
  • 也可以通过运算符比较两个字符串
using System;

namespace StringApplication
{
   class StringProg
   {
      static void Main(string[] args)
      {
         string str1 = "This is test";
         string str2 = "This is text";

         if (str1 == str2)
         {
            Console.WriteLine(str1 + " and " + str2 +  " are equal.");
         }
         else if(str1 != str2)
         {
            Console.WriteLine(str1 + " and " + str2 + " are not equal.");
         }
         Console.ReadKey() ;
      }
   }
}
2.7.*.2 查找字符串中指定的字符串
using System;

namespace StringApplication
{
    class StringProg
    {
        static void Main(string[] args)
        {
            string str = "This is test";
            if (str.Contains("test"))
            {
                Console.WriteLine("The sequence 'test' was found.");
            }
            Console.ReadKey();
        }
    }
}
2.7.*.3 获取子字符串(切片)
using System;
namespace StringApplication
{
    class StringProg
    {
        static void Main(string[] args)
        {
            string str = "Last night I dreamt of San";
            Console.WriteLine(str);
            string substr = str.Substring(23); // 获取子字符串
            Console.WriteLine(substr);
            Console.ReadKey();
        }
    }
}
2.7.*.4 拼接字符串
using System;

namespace StringApplication
{
    class StringProg
    {
        static void Main(string[] args)
        {
            string[] starray = new string[]{"Down the way nights are dark",
                                             "And the sun shines daily on the mountain top",
                                             "I took a trip on a sailing ship",
                                             "And when I reached Jamaica",
                                             "I made a stop"};

            string str = String.Join("\n", starray); // 插入换行符
            Console.WriteLine(str);
            Console.ReadKey();
        }   
    }
}

2.8 结构体(Struct)

在 C# 中,结构体属于值类型数据结构。它可以存储各种数据类型的相关数据。这一点与C/C++原理相近。

2.8.1 定义结构体/结构体变量

在 C# 中的结构体与 C/C++ 中的结构体不同的地方为:

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

// 定义结构体
struct Books
{
    public string title;
    public string author;
    public string subject;
    public int book_id;
};

public class testStructure
{
    public static void Main(string[] args)
    {
		// C#中定义结构体变量不需要前面加关键字 struct
        Books Book1;        /* 声明结构体变量 Book1,类型为 Books */
        Books Book2;        /* 声明结构体变量 Book2,类型为 Books */

        /* book 1 详述 */
        Book1.title = "C Programming";
        Book1.author = "Nuha Ali";
        Book1.subject = "C Programming Tutorial";
        Book1.book_id = 6495407;

        /* book 2 详述 */
        Book2.title = "Telecom Billing";
        Book2.author = "Zara Ali";
        Book2.subject = "Telecom Billing Tutorial";
        Book2.book_id = 6495700;

        /* 打印 Book1 信息 */
        Console.WriteLine("Book 1 title : {0}", Book1.title);
        Console.WriteLine("Book 1 author : {0}", Book1.author);
        Console.WriteLine("Book 1 subject : {0}", Book1.subject);
        Console.WriteLine("Book 1 book_id :{0}", Book1.book_id);

        /* 打印 Book2 信息 */
        Console.WriteLine("Book 2 title : {0}", Book2.title);
        Console.WriteLine("Book 2 author : {0}", Book2.author);
        Console.WriteLine("Book 2 subject : {0}", Book2.subject);
        Console.WriteLine("Book 2 book_id : {0}", Book2.book_id);

        Console.ReadKey();
    }
}

2.8.2 结构体与类的区别

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

  • 类是引用类型,结构是值类型。
  • 结构不支持继承。
  • 结构不能声明默认的构造函数。
using System;
using System.Text;
     
struct Books
{
   private string title;
   private string author;
   private string subject;
   private int book_id;
   public void setValues(string t, string a, string s, int id)
   {
      title = t;
      author = a;
      subject = s;
      book_id =id; 
   }
   public void display()
   {
      Console.WriteLine("Title : {0}", title);
      Console.WriteLine("Author : {0}", author);
      Console.WriteLine("Subject : {0}", subject);
      Console.WriteLine("Book_id :{0}", book_id);
   }

};  

public class testStructure
{
   public static void Main(string[] args)
   {

      Books Book1 = new Books(); /* 声明 Book1,类型为 Books */
      Books Book2 = new Books(); /* 声明 Book2,类型为 Books */

      /* book 1 详述 */
      Book1.setValues("C Programming",
      "Nuha Ali", "C Programming Tutorial",6495407);

      /* book 2 详述 */
      Book2.setValues("Telecom Billing",
      "Zara Ali", "Telecom Billing Tutorial", 6495700);

      /* 打印 Book1 信息 */
      Book1.display();

      /* 打印 Book2 信息 */
      Book2.display(); 

      Console.ReadKey();

   }
}

2.9 枚举(Enum)

枚举是一组命名整型常量。与结构体一样,C# 的枚举属于值类型。即枚举包含自己的值,且不能继承或传递继承。

using System;

public class EnumTest
{
    enum Day { Sun, Mon, Tue, Wed, Thu, Fri, Sat }; // 定义枚举

    static void Main()
    {
        int x = (int)Day.Sun;
        int y = (int)Day.Fri;
        Console.WriteLine("Sun = {0}", x);
        Console.WriteLine("Fri = {0}", y);
    }
}

2.10 数据类型转换

string 相关的转化

  1. intstring
int a = 15;
string s1 = a.ToString(); // 方法1
string s2 = Convert.ToString(a); // 方法2

3. 变量


一个变量是一个供程序操作的内存区的名字。在 C# 中,每个变量都有一个指定的类型,此类型决定了该变量的内存大小和布局。
C# 中提供的基本的变量类型可分为以下几类:

类型举例
整数类型sbyte、byte(0-255)、short(-32768-32767)、ushort、int(-2147483648-2147483647)、uint、long、ulong 和 char
浮点型float 和 double(double是所有类型中范围最大的)
十进制类型decimal,它也是浮点型的一种,精度比float和double高,float和double总是存在精度损失,而decimal则不会
布尔类型true 或 false
字符串类型string、char
空类型null,可为空值的数据类型
// 变量定义
int i, j, k;
char c, ch;
float f, salary;
double d;
// 变量定义并初始化
int i = 100;
float f = 1.1F; // 单精度浮点型数值必须加后缀F
double d = 1.1;
decimal de = 1.1M; // 十进制类型数值必须加后缀M
string s = "hellow world!"; // 字符串使用""包住
char c = 'h'; // 字符使用''包住

// null
string st = "hellow world!";
st = null;
st = "";
//以上都表示将字符串st清空,但不同的是null表示将字符串彻底清空,而""表示将一个空的字符串赋值给字符串st(空字符串也是一个字符串)
//变量类型中,只有string是可以为null的 

3.1 变量类型转换

  • 类型范围小的变量可赋值给类型范围大的变量,反之则报错。
// 变量定义
int i =0;
long l =0;

l=i; // 允许
i=l; // 报错
  • 浮点型的转换只有float赋值给double一种。

3.2 强制类型转换

3.3 变量作用域

  • 在函数内定义的局部变量只在其函数内作用。
  • 在类内函数外定义的变量可用在类内的函数总,但当该函数中存在同名变量时,则报错。

3.4 变量修饰符

  • 无修饰符:即变量类型无修饰符修饰。
  • out:传入参数必须由被调用的方法赋值修改,以此按引用传递。其原理近似于一个指针。
  • ref:传入参数在被方法调用前必须赋有初值,并输入参数在被调用的方法内可被修改也可不修改(数据是按引用传递的)。

  • outref的区别:
    • out 修饰的参数必须在方法内进行修改,而ref无该要求。
    • 调用方法的输入参数若是局部变量,out修饰的输入参数可无初始值,因为out一定会在方法内对其赋值。而ref修饰的输入参数只有在实参有初始值时才能被调用,因为ref不一定在方法内对其赋值。

3.4.1 out修饰符展示

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        { 
            string message = "hellow world!";
            MessageBox.Show(message);
            SendMesage(out message);
            MessageBox.Show(message);
        }

        public void SendMesage(out string message) // 输入变量为指向message变量的内存地址
        {
            message = "世界你好!"; // 该内存地址的值发生改变,out引用的参数必须在函数内进行赋值,这一点与ref不同
            MessageBox.Show(message);
        }
    }
}

运行结果:输出一次hellow world!,两次世界你好!
  • 另一种用法:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        { 
            SendMesage(out string message); // 在调用函数时定义变量,输入参数允许没有初始值,这一点与ref不同
            MessageBox.Show(message);
        }

        public void SendMesage(out string message)
        {
            message = "世界你好!";
            MessageBox.Show(message);
        }
    }
}


3.4.2 ref修饰符展示

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string message = "hellow world!";
            SendMesage(ref message); // ref的输入参数必须要有初始值
            MessageBox.Show(message);
        }

        public void SendMesage(ref string message)
        {
            message = "世界你好!"; // ref的传入参数,在函数内可被修改,也可不被修改,这一点与out不同
            MessageBox.Show(message);
        }
    }
}

4. 常量


常量是固定值,在程序执行期间不会也不能改变。常量可以是任何基本数据类型,比如整数常量、浮点常量、字符常量或者字符串常量,还有枚举常量。

4.1 整型常量

整数常量可以是十进制、八进制或十六进制的常量。
其前缀指定了其进制类型:0x 或 0X 表示十六进制,0 表示八进制,没有前缀则表示十进制。

其后缀制定了其数据类型,可以是 U 或 L ,其中,U 和 L 分别表示 unsigned 和 long。可以多个后缀以任意顺序进行组合。后缀部分大小写。

212         /* 合法 */
215u        /* 合法 */
0xFeeL      /* 合法 */
078         /* 非法:8 不是一个八进制数字 */
032UU       /* 非法:不能重复后缀 */

85         /* 十进制 */
0213       /* 八进制 */
0x4b       /* 十六进制 */
30         /* int */
30u        /* 无符号 int */
30l        /* long */
30ul       /* 无符号 long */

4.2 浮点型常量

一个浮点常量是由整数部分、小数点、小数部分和指数部分组成。可使用小数形式或指数形式来表示浮点常量。

3.14159       /* 合法 */
314159E-5L    /* 合法 */
210f          /* 非法:没有小数或指数 */
.e55          /* 非法:缺少整数或小数 */

使用小数形式表示时,必须包含小数点或指数或同时包含两者。
使用指数形式表示时,必须包含整数部分或小数部分或同时包含两者。

4.3 字符型常量

字符常量括在单引号里,如'x'。一个字符常量可以是一个普通字符(如 'x')或一个转义序列(如 '\t')或一个通用字符(如 '\u02C0')。

在 C# 中有一些特定的字符,当它们的前面带有反斜杠时有特殊的意义,可用于表示换行符(\n)或制表符 tab(\t)。

转义序列码有:

转义序列含义
| \ 字符
’ 字符
"" 字符
?? 字符
\aAlert 或 bell
\b退格键(Backspace)
\f换页符(Form feed)
\n换行符(Newline)
\r回车
\t水平制表符 tab
\v垂直制表符 tab
\ooo一到三位的八进制数
\xhh . . .一个或多个数字的十六进制数

4.4 字符串型常量

字符串常量括在双引号 "" 里,或括在 @"" 里。字符串常量包含的字符与字符常量相似,可以是:普通字符、转义序列和通用字符

字符串常量可把一个很长的行拆成多个行,可以使用空格分隔各个部分。

string a = "hello, world";                  
string b = @"hello, world";             
string j = @"one
                        two
                           three";

4.5 常量的定义

使用 关键字const 来定义常量 。

const int c1 = 5;

5. 运算符


C# 内置的运算符分类如下:

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 赋值运算符
  • 其他运算符

5.1 算术运算符

运算符描述
+把两个操作数相加
-从第一个操作数中减去第二个操作数
*把两个操作数相乘
/分子除以分母
%取模运算符,整除后的余数
++自增运算符,整数值增加 1
自减运算符,整数值减少 1

5.1.1 关于i++与++i

与C、C++语法同理:

  • c = i++;:先将 i赋值给 c,再对 i 进行自增运算。
  • c = ++i; :先将 i 进行自增运算,再将 i 赋值给 c 。

5.2 关系运算符

运算符描述
==检查两个操作数的值是否相等,如果相等则条件为真。
!=检查两个操作数的值是否相等,如果不相等则条件为真。
>检查左操作数的值是否大于右操作数的值,如果是则条件为真。
<检查左操作数的值是否小于右操作数的值,如果是则条件为真。
>=检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。
<=检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。

5.3 逻辑运算符

运算符描述
&&逻辑与运算符。如果两个操作数都非零,则条件为真。
`
!逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。

5.4 位运算符

运算符作用于逐位执行操作。
以下假设 A = 0011 1100、B = 0000 1101

运算符描述实例
&位与,如果同时存在于两个操作数中,二进制 AND 运算符复制一位到结果中。(A & B) 将得到 12,即为 0000 1100
``位或,如果存在于任一操作数中,二进制 OR 运算符复制一位到结果中。
^异或运算符,当两个二进制数不同时,结果为1。(A ^ B) 将得到 49,即为 0011 0001
~按位取反运算符是一元运算符,即0变成1,1变成0,包括符号位。(~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。
<<二进制左移运算符。左操作数的值向左移动右操作数指定的位数。A << 2 将得到 240,即为 1111 0000
>>二进制右移运算符。左操作数的值向右移动右操作数指定的位数。A >> 2 将得到 15,即为 0000 1111

5.5 赋值运算符

运算符描述实例
=简单的赋值运算符,把右边操作数的值赋给左边操作数
+=加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数C += A 相当于 C = C + A
-=减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数C -= A 相当于 C = C - A
*=乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数C *= A 相当于 C = C * A
/=除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数C /= A 相当于 C = C / A
%=求模且赋值运算符,求两个操作数的模赋值给左边操作数C %= A 相当于 C = C % A
<<=左移且赋值运算符C <<= 2 等同于 C = C << 2
>>=右移且赋值运算符C >>= 2 等同于 C = C >> 2
&=按位与且赋值运算符C &= 2 等同于 C = C & 2
^=按位异或且赋值运算符C ^= 2 等同于 C = C ^ 2
`=`按位或且赋值运算符

5.6 其他运算符

运算符描述实例
sizeof()返回数据类型的大小。sizeof(int),将返回 4.
typeof()返回 class 的类型。typeof(StreamReader);
&返回变量的地址。&a; 将得到变量的实际地址。
*变量的指针。*a; 将指向一个变量。
?:条件表达式如果条件为真 ? 则为 X : 否则为 Y
is判断对象是否为某一类型,返回值为bool类型If( Ford is Car) // 检查 Ford 是否是 Car 类的一个对象。
as强制转换,即使转换失败也不会抛出异常。Object obj = new StringReader(“Hello”); StringReader r = obj as StringReader;
x.m成员访问
x{…}数组和索引器访问
new T(…)对象和委托创建
new T(…){…}使用初始值设定项创建对象
new{…}匿名对象初始值设定
new T{…}数组创建
(T)x将x显式转换为T

5.7 运算符优先级

下表按运算符优先级从高到低列出各个运算符,在表达式中,较高优先级的运算符会优先被计算。

类别运算符结合性
后缀() 、[] 、-> 、.、 ++、 - -从左到右
一元+ 、-、 !、 ~、 ++、 - -、 (type)*、 &、 sizeof,一元表达式即其运算的变量只能有一个从右到左
乘除* 、/、 %从左到右
加减+ 、-从左到右
移位<< 、>>从左到右
关系< 、<=、 >、 >=从左到右
相等==、 !=从左到右
位与 AND&从左到右
位异或 XOR^从左到右
位或 OR``
逻辑与 AND&&从左到右
逻辑或 OR`
条件?:从右到左
赋值= 、+=、 -=、 *=、 /=、 %=、>>=、 <<=、 &= 、^=、 `=`
逗号,从左到右

6. 三大结构


与C语言的三大结构相同:顺序、选择、循环。顺序结构略讲。

6.1 条件结构

语句描述
if 语句一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。
if…else 语句一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。
嵌套 if 语句您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句。
switch 语句一个 switch 语句允许测试一个变量等于多个值时的情况。
嵌套 switch 语句您可以在一个 switch 语句内使用另一个 switch 语句。

6.1.1 switch语句

语法:

switch(expression){
    case constant-expression  :
       statement(s);
       break; 
    case constant-expression  :
       statement(s);
       break; 
  
    /* 可以有任意数量的 case 语句 */
    default : /* 可选的 */
       statement(s);
       break; 
}

其中:

  • switch 语句中的expression 必须是一个整型或枚举类型或是一个 class 类型,其中 class 有一个单一的转换函数将其转换为整型或枚举类型。
  • case 的 constant-expression 必须与 switch 中的变量具有相同的数据类型,且必须是一个常量。
  • 当遇到 break 语句时,switch语句终止,控制流将跳转到 switch 语句后的下一行。
  • 不是每一个 case 都需要包含 break。如果 case 语句为空,则可以不包含 break,控制流将会继续后续的 case,直到遇到 break 为止。
  • 一个 switch 语句可以有一个可选的 default case,必须出现在 switch 的结尾。default case 可用于在上面所有 case 都不为真时执行的一个任务。default case 中的 break 语句不是必需的。

流程图:
在这里插入图片描述

6.1.2 条件运算符 ?:

条件运算符 ?:,可用来替代 if...else 语句。

Exp1 ? Exp2 : Exp3; // 其中Expx为表达式

该语句的结果取决于Exp1的值:

  • 如果 Exp1 为真,则计算 Exp2 的值,结果即为整个?:表达式的值。
  • 如果 Exp1 为假,则计算 Exp3 的值,结果即为整个 ?: 表达式的值。

6.2 循环结构

循环类型描述
while 循环当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。
for/foreach 循环多次执行一个语句序列,简化管理循环变量的代码。
do…while 循环除了它是在循环主体结尾测试条件外,其他与 while 语句类似。
嵌套循环可以在 while、for 或 do…while 循环内使用一个或多个循环。

6.2.1 for/foreach 循环

// for语句语法例程
for (int a = 10; a < 20; a = a + 1)
{
	Console.WriteLine("a 的值: {0}", a);
}
int[] fibarray = new int[] { 0, 1, 1, 2, 3, 5, 8, 13 };
foreach (int element in fibarray)
{
	System.Console.WriteLine(element);
}
	System.Console.WriteLine();

输出:
0
1
1
2
3
5
8
13

Foreach 语句官方文档
C# 中 foreach 遍历的用法

6.2.2 循环控制语句

控制语句描述
break 语句终止循环或 switch 语句,程序流继续执行紧接着循环或 switch 的下一条语句。即跳出当前循环语句
continue 语句跳出语句的本次循环

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1475927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ubuntu常见配置

ubuntu各个版本的安装过程大差小不差&#xff0c;可以参考&#xff0c;ubuntu20.04 其它版本换一下镜像版本即可 安装之后需要配置基本的环境&#xff0c;我的话大概就以下内容&#xff0c;后续可能有所删改 sudo apt-get update sudo apt-get install gcc sudo apt-get inst…

【踩坑】PyTorch中指定GPU不生效和GPU编号不一致问题

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 指定GPU不生效问题 解释&#xff1a;就是使用os.environ["CUDA_VISIBLE_DEVICES"] "1"后&#xff0c;后面使用起来仍然是cuda0. 解决&#xff1a;在最开头就使用 import os os.environ[&…

【MySQL | 第一篇】undo log、redo log、bin log三者之间的区分?

undo log、redo log、bin log三者之间的区分&#xff1f; 从 产生的时间点、日志内容、用途 三方面展开论述即可 1.undo log——撤销日志 时间点&#xff1a;事务开始之前产生&#xff0c;根据当前版本的数据生成一个undo log&#xff0c;也保存在事务开始之前 作用&#xf…

springboot+vue网站开发-渲染前端列表页面-缩略图信息

springbootvue网站开发-渲染前端列表页面-缩略图信息&#xff01;内容比较多。这是第一篇&#xff0c;先给大家展示的是&#xff0c;基础的代码封装&#xff0c;vue前端网站模块的代码展示。 我们使用到了pinia-存储我们请求过来的数据&#xff0c;它是一个状态管理&#xff0c…

非线性优化-高斯牛顿法

在SLAM领域&#xff0c;后端多采用基于非线性优化的方法&#xff0c;来优化位姿和地图点&#xff0c;其中高斯牛顿法的使用频率很高。 求解高斯牛顿法的核心公式&#xff1a; 其中 f 是误差函数&#xff0c;J是误差关于待优化变量的雅可比矩阵。 其中H为海森矩阵&#xff08…

RRT算法学习及MATLAB演示

文章目录 1 前言2 算法简介3 MATLAB实现3.1 定义地图3.2 绘制地图3.3 定义参数3.4 绘制起点和终点3.5 RRT算法3.5.1 代码3.5.2 效果3.5.3 代码解读 4 参考5 完整代码 1 前言 RRT&#xff08;Rapid Random Tree&#xff09;算法&#xff0c;即快速随机树算法&#xff0c;是LaVa…

C语言第三十二弹---自定义类型:联合和枚举

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 目录 1、联合体 1.1、联合体类型的声明 1.2、联合体的特点 1.3、相同成员的结构体和联合体对比 1.4、联合体大小的计算 1.5、联合的⼀个练习 2、枚举类型 …

176基于matlab的自适应滤波法预测

基于matlab的自适应滤波法预测&#xff0c;自适应滤波预测实质上是一种加权滑动平均预测&#xff0c;通过迭代得到最佳权值&#xff0c;并给出了相对误差图和预测效果图&#xff0c;程序已调通&#xff0c;可直接运行。 176matlab自适应滤波法预测 时间序列预测 (xiaohongshu.c…

51单片机(6)-----直流电机的介绍与使用(通过独立按键控制电机的运行)

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 目录 一. 直流电机模块介绍 1.直流电机介绍 2.电机参数 二. 程序设计…

java线程池原理源码解析,程序员如何技术划水

前言 面试大概九十分钟&#xff0c;问的东西很全面&#xff0c;需要做充足准备&#xff0c;就是除了概念以外问的有点懵逼了。回来之后把这些题目做了一个分类并整理出答案&#xff08;强迫症的我~狂补知识&#xff09;分为MySQLJavaRedis算法网络Linux等六类&#xff0c;接下…

2024-02-28(Kafka,Oozie,Flink)

1.Kafka的数据存储形式 一个主题由多个分区组成 一个分区由多个segment段组成 一个segment段由多个文件组成&#xff08;log&#xff0c;index&#xff08;稀疏索引&#xff09;&#xff0c;timeindex&#xff08;根据时间做的索引&#xff09;&#xff09; 2.读数据的流程 …

Swagger接口文档管理工具

Swagger 1、Swagger1.1 swagger介绍1.2 项目集成swagger流程1.3 项目集成swagger 2、knife4j2.1 knife4j介绍2.2 项目集成knife4j 1、Swagger 1.1 swagger介绍 官网&#xff1a;https://swagger.io/ Swagger 是一个规范和完整的Web API框架&#xff0c;用于生成、描述、调用和…

textbox跨线程写入

实现实例1 实现效果 跨线程实现 // 委托&#xff0c;用于定义在UI线程上执行的方法签名 //public delegate void SetTextCallback(string text);public void textBoxText(string text){// 检查调用线程是否是创建控件的线程 if (textBox1.InvokeRequired){// 如果不是&#…

React UI框架Antd 以及 如何按需引入css样式配置(以及过程中各种错误处理方案)

一、react UI框架Antd使用 1.下载模块 npm install antd -S 2.引入antd的样式 import ../node_modules/antd/dist/reset.css; 3.局部使用antd组件 import {Button, Calendar} from antd; import {PieChartTwoTone} from ant-design/icons; {/* 组件汉化配置 */} import l…

Ant for Blazor做单个表的增删查改

Ant for Blazor做单个表的增删查改 2024年02月27日花了一天时间弄出来了&#xff0c;基本弄好了&#xff0c;vs2022blazor servernet8,引用的AntDesign版本是0.17.4 代码里的model和repository是用自己牛腩代码生成器生成的东西&#xff0c;sqlsugar的&#xff0c;记得在prog…

ROS 2基础概念#1:计算图(Compute Graph)| ROS 2学习笔记

在ROS中&#xff0c;计算图&#xff08;ROS Compute Graph&#xff09;是一个核心概念&#xff0c;它描述了ROS节点之间的数据流动和通信方式。它不仅仅是一个通信网络&#xff0c;它也反映了ROS设计哲学的核心——灵活性、模块化和可重用性。通过细致探讨计算图的高级特性和实…

面试数据库篇(mysql)- 12分库分表

拆分策略 垂直分库 垂直分库:以表为依据,根据业务将不同表拆分到不同库中。 特点: 按业务对数据分级管理、维护、监控、扩展在高并发下,提高磁盘IO和数据量连接数垂直分表:以字段为依据,根据字段属性将不同字段拆分到不同表中。 特点: 1,冷热数据分离 2,减少IO过渡争…

CSS——PostCSS简介

文章目录 PostCSS是什么postCSS的优点补充&#xff1a;polyfill补充&#xff1a;Stylelint PostCSS架构概述工作流程PostCSS解析方法PostCSS解析流程 PostCSS插件插件的使用控制类插件包类插件未来的CSS语法相关插件后备措施相关插件语言扩展相关插件颜色相关组件图片和字体相关…

Rocky Linux安装部署Elasticsearch(ELK日志服务器)

一、Elasticsearch的简介 Elasticsearch是一个强大的开源搜索和分析引擎&#xff0c;可用于实时处理和查询大量数据。它具有高性能、可扩展性和分布式特性&#xff0c;支持全文搜索、聚合分析、地理空间搜索等功能&#xff0c;是构建实时应用和大规模数据分析平台的首选工具。 …

车牌识别-只用opencv的方式

项目简述 本文描述如何只使用opencv将车牌中的车牌号提取出来&#xff0c;整个过程大致分为三个过程&#xff1a;车牌定位&#xff0c;车牌号元素分割&#xff0c;模式匹配。 在做完这个实验后&#xff0c;我感触是&#xff0c;只用opencv的方式能使用的场景有限&#xff0c;不…