• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

C#学习笔记

开发技术 开发技术 5天前 14次浏览
目录
  • C#
    • 基础程序结构
    • 基本语法
    • 数据类型
      • 装箱和拆箱
      • 动态(Dynamic)类型
      • 字符串(String)类型
      • 指针类型(Pointer types)
    • 类型转换
      • 隐式转换:
      • 显式转换:
      • 强制转换类型的方法
    • 变量
      • 接受来自用户的值
      • 不同类型变量进行运算的问题:
      • 静态变量
      • 常量
    • 定义常量
      • 静态常量(编译时常量)c++onst
      • 动态常量(运行时常量)readonly
    • 封装
    • 方法
      • 参数传递
        • 值传递类型实例:
        • 传递引用参数类型实例:
        • 输出参数类型实例
    • 可空类型
      • 可空申明?
      • 合并运算符??
    • 数组
      • 一维数组
        • 初始化
        • 赋值:
        • 访问数组元素
      • 多维数组
        • 初始化二维数组
        • 循环打印出二维数组:
      • 交错数组
        • 声明
        • 赋值
    • 结构体
      • 定义:
      • 用法
      • 特点
      • 类 vs 结构
      • 补充:类与结构体的区别
    • 枚举enum
      • 类中的构造函数
      • 类中的析构函数
      • 静态成员变量
      • 静态成员函数

C#

基础程序结构

using System;// using 关键字用于在程序中包含 System 命名空间。 一个程序一般有多个 using 语句。
namespace HelloWorldApplication //一个 namespace 里包含了一系列的类。HelloWorldApplication 命名空间包含了类 HelloWorld。
{
   /*class 声明。类 HelloWorld 包含了程序使用的数据和方法声明。类一般包含多个方法。方法定义了类的行为。在这里,HelloWorld 类只有一个 Main 方法。*/
   class HelloWorld
   {
      static void Main(string[] args)
      {
         /* 我的第一个 C# 程序*/
         Console.WriteLine("Hello World");
         Console.ReadKey();//Console.ReadKey(); 是针对 VS.NET 用户的。这使得程序会等待一个按键的动作,防止程序从 Visual Studio .NET 启动时屏幕会快速运行并关闭。
      }
   }
}

基本语法

using System;
namespace RectangleApplication
{
    class Rectangle
    {
        // 成员变量
        double length;//声明一个double类型的变量length 
        double width;//声明一个double类型的变量width
        public void Acceptdetails() //一个用来赋值的方法
        {
            length = 4.5;    
            width = 3.5;
        }
        public double GetArea() //一个用来计算的方法
        {
            return length * width;
        }
        public void Display()  //一个用来打印的方法
        {
            Console.WriteLine("Length: {0}", length);
            Console.WriteLine("Width: {0}", width);
            Console.WriteLine("Area: {0}", GetArea()); //打印GetArea方法的计算结果
        }
    }
    
    class ExecuteRectangle
    {
        static void Main(string[] args) //程序入口方法,创建实例,调用相应的方法
        {
            Rectangle r = new Rectangle();
            r.Acceptdetails();
            r.Display();
            Console.ReadLine();
        }
    }
}

在这段代码中,逻辑是这样的:

  • 首先进入 Main 方法,创建一个名称为 r 的实例。
  • 然后调用 Acceptdetails 给 r 进行赋值。
  • 最后调用 Display 方法打印结果。
  • 而用于计算的 GetArea 方法在在调用 Display 时直接打印出来。

数据类型

类型 描述 范围 默认值
bool 布尔值 True 或 False False
byte 8 位无符号整数 0 到 255 0
char 16 位 Unicode 字符 U +0000 到 U +ffff ‘’
decimal 128 位精确的十进制值,28-29 有效位数 (-7.9 x 1028 到 7.9 x 1028) / 100 到 28 0.0M
double 64 位双精度浮点型 (+/-)5.0 x 10-324 到 (+/-)1.7 x 10308 0.0D
float 32 位单精度浮点型 -3.4 x 1038 到 + 3.4 x 1038 0.0F
int 32 位有符号整数类型 -2,147,483,648 到 2,147,483,647 0
long 64 位有符号整数类型 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 0L
sbyte 8 位有符号整数类型 -128 到 127 0
short 16 位有符号整数类型 -32,768 到 32,767 0
uint 32 位无符号整数类型 0 到 4,294,967,295 0
ulong 64 位无符号整数类型 0 到 18,446,744,073,709,551,615 0
ushort 16 位无符号整数类型 0 到 65,535 0

装箱和拆箱

int val = 8;
object obj = val;//整型数据转换为了对象类型(装箱)

拆箱:之前由值类型转换而来的对象类型再转回值类型, 实例:

int val = 8;
object obj = val;//先装箱
int nval = (int)obj;//再拆箱

只有装过箱的数据才能拆箱

动态(Dynamic++)类型

您可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。

声明动态类型的语法:

dynamic <variable_name> = value;

例如:

dynamic d = 20;

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

字符串(String)类型

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

例如:

String str = "runoob.com";

一个 @引号字符串:

@"runoob.com";

C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符()当作普通字符对待,比如:

string str = @"C:Windows";

等价于:

string str = "C:\Windows";

@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。

string str = @"<script type=""text/javasc++ript"">
    <!--
    -->
</script>";

指针类型(Pointer types)

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

声明指针类型的语法:

type* identifier;

例如:

char* cptr;
int* iptr;

类型转换

类型转换从根本上说是类型铸造,或者说是把数据从一种类型转换为另一种类型。在 C# 中,类型铸造有两种形式:

  • 隐式类型转换 – 这些转换是 C# 默认的以安全方式进行的转换, 不会导致数据丢失。例如,从小的整数类型转换为大的整数类型,从派生类转换为基类。
  • 显式类型转换 – 显式类型转换,即强制类型转换。显式转换需要强制转换运算符,而且强制转换会造成数据丢失。

隐式转换:

C# 默认的以安全方式进行的转换。本质是从小存储容量数据类型自动转换为大存储容量数据类型,从派生类转换为基类。

实例:

namespace TypeConvertion
{   class Class1
    {

    }
    class Class2 : Class1 //类Class2是类Class1的子类
    {

    }
    class Program
    {
        static void Main(string[] args)
        {
            int inum = 100;
            long lnum = inum; // 进行了隐式转换,将 int 型(数据范围小)数据转换为了 long 型(数据范围大)的数据
            Class1 c1 = new Class2(); // 这里也是隐式转换,将一个新建的 Class2 实例转换为了其基类 Class1 类型的实例 C1
        }
    }
}

显式转换:

通过用户使用预定义的函数显式完成的,显式转换需要强制转换运算符。转换类型的范围大小和从属关系和隐式转换相反。显式转换可能会导致数据出错,或者转换失败,甚至无法编译成功。

实例:

double dnum = 100.1;
int ifromd = (int)dnum; //double类型显式转换转为int类型


Class1 c11 = new Class1();
Class2 c22 = c11 as Class2; //使用as进行显式转换
Console.WriteLine(c22 is Class1);
Console.WriteLine(c22 is Class2);

/*运行结果:
FALSE
FALSE
*/

强制转换类型的方法

  1. Convert 和 Parse

    string locstr = 123.ToString();
    
    //如果要将"locstr"转成整型数
    
    //方法一: 用 Convert 
    int i = Convert.ToInt16(locstr);
    
    //方法二: 用 Parse
    int ii = int.Parse(locstr);
    
  2. TryParse

    int.TryParse(string s, out int i)
    

    该方式也是将数字内容的字符串转换为int类型,但是该方式比int.Parse(string s) 好一些,它不会出现异常,最后一个参数result是输出值,如果转换成功则输出相应的值,转换失败则输出0。

    class test
    {
        static void Main(string[] args)
        {
            string s1="abcd";
            string s2="1234";
            int a,b;
            bool bo1 = int.TryParse(s1, out a);
            Console.WriteLine(s1 + " " + bo1 + " " + a);
            bool bo2 = int.TryParse(s2,out b);
            Console.WriteLine(s2+" "+bo2+" "+b);
        }
    }
    

    结果输出:

    abcd False 0
    1234 True 1234
    

变量

接受来自用户的值

System 命名空间中的 Console 类提供了一个函数 ReadLine(),用于接收来自用户的输入,并把它存储到一个变量中。

例如:

int num;
num = Convert.ToInt32(Console.ReadLine());

函数 Convert.ToInt32() 把用户输入的数据转换为 int 数据类型,因为 Console.ReadLine() 只接受字符串格式的数据。

不同类型变量进行运算的问题:

double a = 42.29;
int b = 4229;
int c = a + b;
Console.WriteLine("c = {0}",c);
Console.ReadKey();

上面这种编程方法是错误的,会出现错误提示:

"无法将类型'double'隐式转换为'int'。"

举例说明,当一个精度高的数据类型与一个精度低的数据类型进行运算时,定义运算结果的变量类型必须与精度最高的变量类型相同。这是为了防止在运算过程中造成数据丢失。

下面是正确代码:

double a = 42.29;
int b = 4229;
double c = a + b;
Console.WriteLine("c = {0}",c);
Console.ReadKey();

能输出运算结果:

c = 4271.29

静态变量

  1. 在 C# 中没有全局变量的概念,所有变量必须由该类的实例进行操作,这样做提升了安全性,但是在某些情况下却显得力不从心。因此,我们在保存一些类的公共信息时,就会使用静态变量。
static <data_type> <variable_name> = value;

在变量之前加上 static 关键字,即可声明为静态变量。

  1. 方法的局部变量必须在代码中显式初始化,之后才能在语句中使用它们的值。此时,初始化不是在声明该变量时进行的,但编译器会通过方法检查所有可能的路径,如果检测到局部变量在初始化之前就使用了它的值,就会产生错误。

例如:

public static int Main(){ //返回值类型int类型 正确的做法是初始化它 int d = 0 或者其他值初始化。
    int d;
    Console.WriteLine(d);
}

static void Main(String[],args){ //void 无返回值,参数类型字符串数组
    
}

在这段代码中,演示了如何定义 Main(),使之返回一个 int 类型的数据,而不是 void。但在编译这些代码时,会得到下面的错误消息:

 Use of unassigned local variable 'd' 

常量

定义常量

常量是使用 const 关键字来定义的 。定义一个常量的语法如下:

const <data_type> <constant_name> = value;
const int c1 = 5;

静态常量(编译时常量)c++onst

在编译时就确定了值,必须在声明时就进行初始化且之后不能进行更改,可在类和方法中定义。定义方法如下:

const double a=3.14;// 正确声明常量的方法
const int b;         // 错误,没有初始化

动态常量(运行时常量)readonly

在运行时确定值,只能在声明时或构造函数中初始化,只能在类中定义。定义方法如下:

class Program
{
    readonly int a=1;  // 声明时初始化
    readonly int b;    // 构造函数中初始化
    Program()
    {
        b=2;
    }
    static void Main()
    {
    }
}

封装

封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。

抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化,封装则使开发实现所需级别的抽象

C# 封装根据具体的需要,设置使用者的访问权限,并通过 访问修饰符 来实现。

一个 访问修饰符 定义了一个类成员的范围和可见性。C# 支持的访问修饰符如下所示:

  • public:所有对象都可以访问;
  • private:对象本身在对象内部可以访问;
  • protected:只有该类对象及其子类对象可以访问
  • internal:同一个程序集的对象可以访问;
  • protected internal:访问限于当前程序集或派生自包含类的类型。

方法

一个方法是把一些相关的语句组织在一起,用来执行一个任务的语句块。每一个 C# 程序至少有一个带有 Main 方法的类。要使用一个方法,您需要:

  • 定义方法
  • 调用方法

实例:

class NumberManipulator
{
   public int FindMax(int num1, int num2)
   {
      /* 局部变量声明 */
      int result;

      if (num1 > num2)
         result = num1;
      else
         result = num2;

      return result;
   } 
   static void Main(string[] args)
   {
      /* 局部变量定义 */
      int a = 100;
      int b = 200;
      int ret;
      NumberManipulator n = new NumberManipulator();

      //调用 FindMax 方法
      ret = n.FindMax(a, b);
      Console.WriteLine("最大值是: {0}", ret );
      Console.ReadLine();
   }
}

您也可以使用类的实例从另一个类中调用其他类的公有方法。例如,方法 FindMax 属于 NumberManipulator 类,您可以从另一个类 Test 中调用它。

using System;

namespace CalculatorApplication
{
    class NumberManipulator
    {
        public int FindMax(int num1, int num2)
        {
            /* 局部变量声明 */
            int result;
            if (num1 > num2)
                result = num1;
            else
                result = num2;
            return result;
        }
    }
    class Test
    {
        static void Main(string[] args)
        {
            /* 局部变量定义 */
            int a = 100;
            int b = 200;
            int ret;
            NumberManipulator n = new NumberManipulator();
            //调用 FindMax 方法
            ret = n.FindMax(a, b);
            Console.WriteLine("最大值是: {0}", ret );
            Console.ReadLine();
        }
    }
}

参数传递

  1. 值传递: 这种方式复制参数的实际值给函数的形式参数,实参和形参使用的是两个不同内存中的值。在这种情况下,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全

  2. 引用参数:这种方式复制参数的内存位置的引用给形式参数。这意味着,当形参的值发生改变时,同时也改变实参的值

  3. 输出参数:这种方式可以返回多个值

值传递类型实例:

class PassingValByVal
{
    private static void Change(int x)
    {
        x = 10;
        System.Console.WriteLine("The value inside the method: {0}", x);
    }
    public static void Execute()
    {
        int n = 5;
        System.Console.WriteLine("The value before calling the method: {0}", n);
        Change(n);
        System.Console.WriteLine("The value after calling the method: {0}", n);
        System.Console.ReadLine();
    }
}
// 输出:
// 5
// 10
// 5

断点执行步骤:

  1. 原变量 n 是 int 值类型,系统为原变量 n 在堆栈Stack上分配的内存地址为:0x088aed2c,该内存地址存储的数据值为5
  2. 当调用 Change 方法时,由于是值传递,系统会为局部参数变量 x 在堆栈Stack上分配一个新的内存区域, 内存地址为:0x088aecd0,并将 n 中的数据值 5 复制到局部参数变量 x 中。这里可以看到局部参数变量 x 和原变量 n 的内存地址是不同的
  3. 对局部参数变量 x 的赋值操作是对变量内存地址中的数据值进行修改,此处是将局部参数变量 x 的内存地址 0x088aecd0 中的数据值由原来的 5 改为 10
  4. 因为原变量 n 和局部参数变量 x 并不是同一块内存区域(内存地址不同),所以 Change 方法中对局部参数变量 x 的赋值操作不会影响到原变量 n。n 的值在调用 Change 方法前后是相同的。实际上,方法内发生的更改只影响局部参数变量 x

传递引用参数类型实例:

class PassingRefByVal
{
    private static void Change(ref int x)
    {
        x = 10;
        System.Console.WriteLine("The value inside the method: {0}", x);
    }
    public static void Execute()
    {
        int n = 5;
        System.Console.WriteLine("The value before calling the method: {0}", n);
        Change(ref n);
        System.Console.WriteLine("The value after calling the method: {0}", n);
        System.Console.ReadLine();
    }
    
    // 输出:
    // 5 
    // 10 
    // 10 

断点执行步骤:

  1. 原变量 n 是 int 值类型,系统为原变量 n 在堆栈Stack上分配的内存地址为:0x0877eb4c,该内存地址存储的数据值为5
  2. 当调用 Change 方法时,由于是引用传递,局部参数变量 x 的内存地址为原变量 n 的内存地址 0x0877eb4c,故局部参数变量 x 的值即是原变量 n 的值。这里可以看到局部参数变量 x 和原变量 n 的内存地址是相同的
  3. 对局部参数变量 x 的赋值操作是对变量内存地址中的数据值进行修改,此处是将局部参数变量 x 的内存地址 0x0877eb4c 中的数据值由原来的 5 改为 10。这里监视窗口中 *&n 的值没有改变是因为在 Change 方法代码块中访问不到原变量 n ,数值没有刷新
  4. 因为原变量 n 和局部参数变量 x 是同一块内存区域(内存地址相同),所以调用 Change 方法后,可以看到变量 n 的内存地址 0x0877eb4c 中的数据值为 10

输出参数类型实例

用法:

  1. 形参与实参之前,都要加out关键字
  2. 输出参数主要是用于函数需要有多个返回值时,作为返回值使用

与引用变量相同的是:

  1. 输出参数也不复制实参在栈中的副本,而是将实参在栈中的地址传给形参。在这点上,输出参数与引用参数相同。
  2. 实参必须是可以赋值的变量,而不能是常量

与引用参数不同的是:

  1. 实参在使用之前不必赋值
  2. 事实上,在使用之前对实参的赋值没有任何意义,因为在调用方法的最开始,便会将其值抹去,使其成为未赋值的变量
  3. 在方法返回之前,必须对out参数进行赋值 

由以上特点所决定的是,输出参数无法向方法中传递信息,其唯一作用便是,当一个参数需要返回多个值时,作为返回值返回。

实例:

 1 class Program
 2     {
 3         static void Main(String[] args)
 4         {
 5             int a;
 6             Add(1, 2, out a);
 7             Console.WriteLine(a);
 8         }
 9         public static bool Add(int x, int y, out int result)
10         {
11             result = x + y;
12             return true;
13         }
14     }
15 //输出:
   // 3

可空类型

可空申明?

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

//实例一:
int? i = 3
int i; //默认值0
int? ii; //默认值null

//实例二:
using System;
namespace CalculatorApplication
{
   class NullablesAtShow
   {
      static void Main(string[] args)
      {
         int? num1 = null;//或使用为new int?()
         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();
      }
   }
}

//输出:
显示可空类型的值: , 45,  , 3.14157
一个可空的布尔值:

合并运算符??

如果第一个操作数的值为 null,则运算符返回第二个操作数的值,否则返回第一个操作数的值

double? a = null;
double b;
b = a?? 888;

数组

一维数组

初始化

double[] balance = new double[10];

赋值:

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 [] marks = new int[]  { 99,  98, 92, 97, 95};
//您也可以赋值一个数组变量到另一个目标数组变量中
int [] marks = new int[]  { 99,  98, 92, 97, 95};
int[] score = marks;

访问数组元素

double salary = balance[9];

多维数组

二维数组类似矩阵

初始化二维数组

int [,] a = new int [3,4] {
 {0, 1, 2, 3} ,   /*  初始化索引号为 0 的行 */
 {4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
 {8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
};

循环打印出二维数组:

using System;
namespace ArrayApplication
{
    class MyArray
    {
        static void Main(string[] args)
        {
            /* 一个带有 5 行 2 列的数组 */
            int[,] a = new int[5, 2] {{0,0}, {1,2}, {2,4}, {3,6}, {4,8} };

            int i, j;

            /* 输出数组中每个元素的值 */
            for (i = 0; i < 5; i++)
            {
                for (j = 0; j < 2; j++)
                {
                    Console.WriteLine("a[{0},{1}] = {2}", i, j, a[i,j]);
                }
            }
           Console.ReadKey();
        }
    }
}

交错数组

维数组的元素的类型是一个数组。

声明

int[][] arr = new int[3][];

赋值

方式一:声明时赋值:

1  int[][] arr = new int[3][]{
2                 new int[] { 1, 2, 3, 4, 5 },
3                 new int[] { 6, 7, 8 },
4                 new int[] { 9, 10 }
5             };

方式二:声明后赋值:

int[][] arr = new int[3][];
arr[0] = new int[] { 1, 2, 3, 4, 5 };
arr[1] = new int[] { 6, 7, 8 };
arr[2] = new int[] { 9, 10 };

为交错数组的元素(数组)的元素赋值

int[][] arr = new int[3][] { new int[3], new int[5], new int[7] };
arr[0][0] = 1;//为第0个数组的第0个元素赋值
arr[1][2] = 15;//为第1个数组的第2个元素赋值
arr[2][3] = 23;//为第2个数组的第3个元素赋值

int[] arrResult = arr[0];

注:取值变量也该声明成数组类型

结构体

在 C# 中,结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。struct 关键字用于创建结构体。结构体是用来代表一个记录。

定义:

struct Books
{
   public string title;
   public string author;
   public string subject;
   public int book_id;
};  

用法

结构可以不使用 New 操作符即可被实例化。

 //声明时不用初始化
  Books Book1;        /* 声明 Book1,类型为 Book */ 
  Books Book2;        /* 声明 Book2,类型为 Book */

  /* 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;

特点

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

类 vs 结构

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

  • 类是引用类型,结构是值类型。
  • 结构不支持继承。
  • 结构不能声明默认的构造函数。

针对上述讨论,让我们重写前面的实例:

using System;
using System.Text;   
struct Books
{
   private string title;
   private string author;
   private string subject;
   private int book_id;
   public void getValues(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,类型为 Book */
      Books Book2 = new Books(); /* 声明 Book2,类型为 Book */
      /* book 1 详述 */
      Book1.getValues("C Programming",
      "Nuha Ali", "C Programming Tutorial",6495407);
      /* book 2 详述 */
      Book2.getValues("Telecom Billing",
      "Zara Ali", "Telecom Billing Tutorial", 6495700);
      /* 打印 Book1 信息 */
      Book1.display();
      /* 打印 Book2 信息 */
      Book2.display();
      Console.ReadKey();
   }
}

补充:类与结构体的区别

1、结构体中声明的字段无法赋予初值,类可以:

struct test001
{
    private int aa = 1;
}

执行以上代码将出现“结构中不能实例属性或字段初始值设定”的报错,而类中无此限制,代码如下:

class test002
{
    private int aa = 1;
}

2、结构体的构造函数中,必须为结构体所有字段赋值,类的构造函数无此限制:

补充:类与结构的选择:

首先明确,类的对象是存储在堆空间中,结构存储在栈中。堆空间大,但访问速度较慢,栈空间小,访问速度相对更快。故而,当我们描述一个轻量级对象的时候,结构可提高效率,成本更低。当然,这也得从需求出发,假如我们在传值的时候希望传递的是对象的引用地址而不是对象的拷贝,就应该使用类了。

枚举enum

枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值。默认情况下,第一个枚举符号的值是 0.例如:

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);
    }
}

//输出
Sun = 0
Fri = 5

自定义每个符号的值:

using System;
namespace EnumApplication
{
    class EnumProgram
    {
        enum Days {
            Mon=71, 
            tue=61, 
            Wed=51, 
            thu=41, 
            Fri=51, 
            Sat=61, 
            Sun=71
        };

        static void Main(string[] args)
        {
            int WeekdayStart = (int)Days.Mon;
            int WeekdayEnd = (int)Days.Fri;
            Console.WriteLine("Monday: {0}", WeekdayStart);
            Console.WriteLine("Friday: {0}", WeekdayEnd);
            Console.ReadKey();
        }
    }
}

//输出
Monday: 71
Friday: 51

注意:

  • 如果没有指定,则使用默认的访问标识符。类的默认访问标识符是 internal,成员的默认访问标识符是 private
  • 数据类型 指定了变量的类型,返回类型 指定了返回的方法返回的数据类型。
  • 如果要访问类的成员,你要使用点(.)运算符。
  • 点运算符链接了对象的名称和成员的名称。

类中的构造函数

类的 构造函数 是类的一个特殊的成员函数,当创建类的新对象时执行。构造函数的名称与类的名称完全相同,它没有任何返回类型。

using System;
namespace LineApplication
{
   class Line
   {
      private double length;   // 线条的长度
      public Line()
      {
         Console.WriteLine("对象已创建");
      }

      public void setLength( double len )
      {
         length = len;
      }
      public double getLength()
      {
         return length;
      }

      static void Main(string[] args)
      {
         Line line = new Line();    
         // 设置线条长度
         line.setLength(6.0);
         Console.WriteLine("线条的长度: {0}", line.getLength());
         Console.ReadKey();
      }
   }
}
//输出
对象已创建	//类创建实例时构造函数就被调用
线条的长度: 6

默认的构造函数没有任何参数。但是如果你需要一个带有参数的构造函数可以有参数,这种构造函数叫做参数化构造函数。这种技术可以帮助你在创建对象的同时给对象赋初始值,具体请看下面实例:

​ Line line = new Line(10.0)

using System;
namespace LineApplication
{
   class Line
   {
      private double length;   // 线条的长度
      public Line(double len)  // 参数化构造函数
      {
         Console.WriteLine("对象已创建,length = {0}", len);
         length = len;
      }

      public void setLength( double len )
      {
         length = len;
      }
      public double getLength()
      {
         return length;
      }

      static void Main(string[] args)
      {
         Line line = new Line(10.0);
         Console.WriteLine("线条的长度: {0}", line.getLength());
         // 设置线条长度
         line.setLength(6.0);
         Console.WriteLine("线条的长度: {0}", line.getLength());
         Console.ReadKey();
      }
   }
}

类中的析构函数

类的 析构函数 是类的一个特殊的成员函数,当类的对象超出范围时执行。析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数。析构函数用于在结束程序(比如关闭文件、释放内存等)之前释放资源。析构函数不能继承或重载。

下面的实例说明了析构函数的概念:

using System;
namespace LineApplication
{
   class Line
   {
      private double length;   // 线条的长度
      public Line()  // 构造函数
      {
         Console.WriteLine("对象已创建");
      }
      ~Line() //析构函数
      {
         Console.WriteLine("对象已删除");
      }

      public void setLength( double len )
      {
         length = len;
      }
      public double getLength()
      {
         return length;
      }

      static void Main(string[] args)
      {
         Line line = new Line();
         // 设置线条长度
         line.setLength(6.0);
         Console.WriteLine("线条的长度: {0}", line.getLength());          
      }
   }
}
//输出
对象已创建
线条的长度: 6
对象已删除

静态成员变量

我们可以使用 static 关键字把类成员定义为静态的。当我们声明一个类成员为静态时,意味着无论有多少个类的对象被创建,只会有一个该静态成员的副本。

关键字 static 意味着类中只有一个该成员的实例。静态变量用于定义常量,因为它们的值可以通过直接调用类而不需要创建类的实例来获取。静态变量可在成员函数或类的定义外部进行初始化。你也可以在类的定义内部初始化静态变量。

using System;
namespace StaticVarApplication
{
    class StaticVar
    {
       public static int num;
        public void count()
        {
            num++;
        }
        public int getNum()
        {
            return num;
        }
    }
    class StaticTester
    {
        static void Main(string[] args)
        {
            StaticVar s1 = new StaticVar();
            StaticVar s2 = new StaticVar();
            s1.count();
            s1.count();
            s1.count();
            s2.count();
            s2.count();
            s2.count();        
            Console.WriteLine("s1 的变量 num: {0}", s1.getNum());
            Console.WriteLine("s2 的变量 num: {0}", s2.getNum());
            Console.ReadKey();
        }
    }
}

//输出
s1 的变量 num: 6
s2 的变量 num: 6

静态成员函数

你也可以把一个成员函数声明为 static。这样的函数只能访问静态变量。静态函数在对象被创建之前就已经存在

using System;
namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            int num = AddClass.Add(2, 3);  //编译通过
            Console.WriteLine(num);
        }
    }

    class AddClass
    {
        public static int Add(int x,int y)
        {
            return x + y;
        }
    }
}

程序员灯塔 , 版权所有
转载请注明原文链接:https://www.wangt.cc/2020/07/c%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0/
喜欢 (0)