C#学习笔记精简篇

C#学习笔记

一、简介

  • C# 是一个现代的、通用的、面向对象的编程语言,它是由微软(Microsoft)开发的,由 Ecma 和 ISO 核准认可的。

  • C# 是由 Anders Hejlsberg 和他的团队在 .Net 框架开发期间开发的。

  • C# 是专为公共语言基础结构(CLI)设计的。CLI 由可执行代码和运行时环境组成,允许在不同的计算机平台和体系结构上使用各种高级语言。

  • 为什么C#应用如此广泛?

    • 现代的、通用的编程语言。

    • 面向对象。

    • 面向组件。

    • 容易学习。

    • 结构化语言。

    • 它产生高效率的程序。

    • 它可以在多种计算机平台上编译。

    • .Net 框架的一部分。

1.1 C#的特性

虽然 C# 的构想十分接近于传统高级语言 C 和 C++,是一门面向对象的编程语言,但是它与 Java 非常相似,有许多强大的编程功能,因此得到广大程序员的青睐。

  • 布尔条件(Boolean Conditions)
  • 自动垃圾回收(Automatic Garbage Collection)
  • 标准库(Standard Library)
  • 组件版本(Assembly Versioning)
  • 属性(Properties)和事件(Events)
  • 委托(Delegates)和事件管理(Events Management)
  • 易于使用的泛型(Generics)
  • 索引器(Indexers)
  • 条件编译(Conditional Compilation)
  • 简单的多线程(Multithreading)
  • LINQ 和 Lambda 表达式
  • 集成 Windows

二、C#的环境

2.1 集成开发环境(IDE)

微软(Microsoft)提供了下列用于 C# 编程的开发工具:

  • Visual Studio 2015 (VS):使用的是该版本
  • Visual C# 2010 Express (VCE)
  • Visual Web Developer

2.2 .net框架(.NET Framework)

.Net 框架应用程序是多平台的应用程序。框架的设计方式使它适用于下列各种语言:C#、C++、Visual Basic、Jscript、COBOL 等等。所有这些语言可以访问框架,彼此之间也可以互相交互。

三、C#程序结构

一个 C# 程序主要包括以下部分:

  • 命名空间声明(Namespace declaration)
  • 一个 class
  • Class 方法
  • Class 属性
  • 一个 Main 方法
  • 语句(Statements)& 表达式(Expressions)
  • 注释

实例:

using System;// using 关键字用于在程序中包含System 命名空间。 一个程序一般有多个 using 语句。

namespace HelloWorld //一个 namespace 里包含了一系列的类。HelloWorld 命名空间包含了类 Program。
{
    /// <summary>
    /// 文档注释
    /// </summary>
    class Program //类 Program 包含了程序使用的数据和方法声明
    {
        // 主方法:程序执行的入口方法
        static void Main(string[] args)
        {
            /*
              多行注释 
              在控制台打印HelloWord 
             */
            Console.WriteLine("HelloWorld!");// WriteLine 是一个定义在 System 命名空间中的 Console 类的一个方法。
            Console.ReadKey();//针对 VS.NET。这使得程序会等待一个按键的动作,防止程序从 Visual Studio .NET 启动时屏幕会快速运行并关闭。
        }
    }
}

运行结果:
在这里插入图片描述

注意

  • C# 是大小写敏感的。
  • 所有的语句和表达式必须以分号(;)结尾。
  • 程序的执行从 Main 方法开始。
  • 与 Java 不同的是,文件名可以不同于类的名称

四、基本语法

4.1 成员变量

变量是类的属性或数据成员,用于存储数据。

4.2 成员函数

函数是一系列执行指定任务的语句。类的成员函数是在类内声明的。

4.3 实例化一个类

对应Java里面的创建(new)对象。

4.4 标识符

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

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

4.5 C#关键字

在这里插入图片描述
在这里插入图片描述

五、数据类型

在 C# 中,变量分为以下几种类型:
在这里插入图片描述

  • 值类型(Value types)

  • 引用类型(Reference types):引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。

    换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:objectdynamicstring

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

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

    object obj;
    obj = 422; // 装箱
    

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

    // 声明动态类型
    dynamic <variable_name> = value;
    // 如
    dynamic d = 100;
    

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

    3、字符串(String)类型

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

    //注意:C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待,比如:
    string str = @"C:\Windows";
    //上面的语句等价于
    string str = "C:\\Windows";
    
    /*
    	@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。
    */
    string str = @"<script type=""text/javascript"">
        <!--
        -->
    </script>";
    
  • 指针类型(Pointer types)

    指针,是C语言中的一个重要概念及其特点,也是掌握C语言比较困难的部分。指针也就是内存地址,指针变量是用来存放内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。

类型转换

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

  • 隐式类型转换 - 这些转换是 C# 默认的以安全方式进行的转换, 不会导致数据丢失。例如,从小的整数类型转换为大的整数类型,从派生类转换为基类(string->Object)。
  • 显式类型转换 - 显式类型转换,即强制类型转换。显式转换需要强制转换运算符,而且强制转换会造成数据丢失。
double d = 3.1415926;
int n = (int)d; // 强制类型转换
object obj = d;
Console.WriteLine(n + "\n" + obj);

六、封装

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

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

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

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

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

6.1 public访问修饰符

public 访问修饰符允许一个类将其成员变量和成员函数暴露给其他的函数和对象。任何公有成员可以被外部的类访问。

6.2 private访问修饰符

private 访问修饰符允许一个类将其成员变量和成员函数对其他的函数和对象进行隐藏。只有同一个类中的函数可以访问它的私有成员。即使是类的实例也不能访问它的私有成员。

6.3 protected访问修饰符

protected 访问修饰符允许子类访问它的基类的成员变量和成员函数。这样有助于实现继承。

6.4 internal访问修饰符

同一个命名空间中(访问仅限于当前程序集,只有当前工程可以存取),Internal 访问说明符允许一个类将其成员变量和成员函数暴露给当前程序中的其他函数和对象。换句话说,带有 internal 访问修饰符的任何成员可以被定义在该成员所定义的应用程序内的任何类或方法访问。

注意:如果没有指定访问修饰符,则使用类成员的默认访问修饰符,即为 private

6.5 protected internal 访问修饰符

Protected Internal 访问修饰符允许在本类,派生类或者包含该类的程序集中访问。这也被用于实现继承。

七、方法

7.1 方法的调用

  1. 使用方法名直接调用方法【有点像在Java中,同一个类中可以调用(static修饰的)静态方法】。
  2. 通过一个类的实例去调用这个类中公共的方法(即使用创建出的对象去调用方法)。
  3. 递归调用:方法自己调用自己。

7.2 参数的传递

7.2.1 按值传递参数

这是参数传递的默认方式。在这种方式下,当调用一个方法时,会为每个值参数创建一个新的存储位置。

实际参数的值会复制给形参,实参和形参使用的是两个不同内存中的值。所以,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。

7.2.3 按引用传递参数(与java有区别的地方)

引用参数是一个对变量的内存位置的引用。当按引用传递参数时,与值参数不同的是,它不会为这些参数创建一个新的存储位置。引用参数表示与提供给方法的实际参数具有相同的内存位置。

在 C# 中,使用 ref 关键字声明引用参数。

实例:对比doSome方法和doOther方法的执行结果可发现按值传参与按引用传递参数的区别。

using System;

namespace Test01
{
    class Program
    {
        public static void doSome(int a,int b)
        {
            a--;b--;
            Console.WriteLine("----{0}  {1}----", a, b);
        }

        public static void doOther(ref int a,int b)
        {
            a--;b--;
            Console.WriteLine("----{0}  {1}----", a, b);
        }

        static void Main(string[] args)
        {
            int a = 10,b=20;
            /*
            doSome(a, b);
            Console.WriteLine("{0}  {1}", a, b);
            */

            doOther(ref a,b);
            Console.WriteLine("{0}  {1}", a, b);
            Console.Read();
        }
    }
}
7.2.3 按输出传递参数

return 语句可用于只从函数中返回一个值。但是,可以使用 输出参数 来从函数中返回两个值。输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。

using System;

namespace Test02
{
    class NumberManipulator
    {
        public void getValue(out int x)
        {
            int temp = 5;
            x = temp;
        }

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

            Console.WriteLine("在方法调用之前,a 的值: {0}", a);// 100

            /* 调用函数来获取值 */
            n.getValue(out a);

            Console.WriteLine("在方法调用之后,a 的值: {0}", a);// 5
            Console.ReadLine();
        }
    }
}

八、可空类型(Nullable)

C# 提供了一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值。

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

在处理数据库和其他包含可能未赋值的元素的数据类型时,将 null 赋值给数值类型或布尔型的功能特别有用。例如,数据库中的布尔型字段可以存储值 true 或 false,或者,该字段也可以未定义。

// 申明可空类型
int a1;//默认值是0
int? a;// 默认值是null
int? b = 12;
bool? flag = null;

8.1 Null合并运算符 ??

Null 合并运算符用于定义可空类型和引用类型的默认值。Null 合并运算符为类型转换定义了一个预设值,以防可空类型的值为 Null。Null 合并运算符把操作数类型隐式转换为另一个可空(或不可空)的值类型的操作数的类型。

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

实例:

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

九、数组

数组的声明

datatype[] arrayName;

初始化数组

声明一个数组不会在内存中初始化数组。当初始化数组变量时,您可以赋值给数组。

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

double[] arrs = 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;

当您创建一个数组时,C# 编译器会根据数组类型隐式初始化每个数组元素为一个默认值。例如,int 数组的所有元素都会被初始化为 0。

十、结构体

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

定义一个结构体:

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

C# 结构的特点

在 C# 中的结构与传统的 C 或 C++ 中的结构不同。C# 中的结构有以下特点:

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

类 vs 结构

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

  • 类是引用类型,结构是值类型。
  • 结构不支持继承。
  • 结构不能声明默认的构造函数。
  • 结构体中声明的字段无法赋予初值,类可以。
  • 结构体的构造函数中,必须为结构体所有字段赋值,类的构造函数无此限制。

类与结构的选择

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

十一、枚举

枚举是一组命名整型常量。枚举类型是使用 enum 关键字声明的。

C# 枚举是值类型。换句话说,枚举包含自己的值,且不能继承或传递继承。

枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值。默认情况下,第一个枚举符号的值是 0。但是,你也可以自定义每个符号的值

// 声明枚举实例
enum Days { Sun, Mon, tue, Wed, thu, Fri, Sat };

十二、C#中的析构函数

类的 析构函数 是类的一个特殊的成员函数,当类的对象超出范围时执行。

析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它没有返回值,也不带任何参数。

析构函数用于在***结束程序(比如关闭文件、释放内存等)之前释放资源***。析构函数不能继承或重载。

十三、继承

当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类,这个新的类被称为派生类

继承的思想实现了 属于(IS-A) 关系。

C#不支持多重继承,但可以通过接口来实现多重继承(这一点和Java是一样的)。

十四、多态性

多态是同一个行为具有多个不同表现形式或形态的能力。

多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。

多态性可以是静态的或动态的。在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。

在 C# 中,每个类型都是多态的,因为包括用户定义类型在内的所有类型都继承自 Object。

多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:
在这里插入图片描述

静态多态性

在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。C# 提供了两种技术来实现静态多态性。分别为:

  • 函数重载:函数名称和返回值类型相同,参数列表不同。
  • 运算符重载:可以重定义或重载 C# 中内置的运算符。
using System;

namespace OperatorOvlApplication
{
   class Box
   {
      private double length;      // 长度
      private double breadth;     // 宽度
      private double height;      // 高度

      public double getVolume()
      {
         return length * breadth * height;
      }
      public void setLength( double len )
      {
         length = len;
      }

      public void setBreadth( double bre )
      {
         breadth = bre;
      }

      public void setHeight( double hei )
      {
         height = hei;
      }
      // 重载 + 运算符来把两个 Box 对象相加
      public static Box operator+ (Box b, Box c)
      {
         Box box = new Box();
         box.length = b.length + c.length;
         box.breadth = b.breadth + c.breadth;
         box.height = b.height + c.height;
         return box;
      }

   }

   class Tester
   {
      static void Main(string[] args)
      {
         Box Box1 = new Box();         // 声明 Box1,类型为 Box
         Box Box2 = new Box();         // 声明 Box2,类型为 Box
         Box Box3 = new Box();         // 声明 Box3,类型为 Box
         double volume = 0.0;          // 体积

         // Box1 详述
         Box1.setLength(6.0);
         Box1.setBreadth(7.0);
         Box1.setHeight(5.0);

         // Box2 详述
         Box2.setLength(12.0);
         Box2.setBreadth(13.0);
         Box2.setHeight(10.0);

         // Box1 的体积
         volume = Box1.getVolume();
         Console.WriteLine("Box1 的体积: {0}", volume);

         // Box2 的体积
         volume = Box2.getVolume();
         Console.WriteLine("Box2 的体积: {0}", volume);

         // 把两个对象相加
         Box3 = Box1 + Box2;

         // Box3 的体积
         volume = Box3.getVolume();
         Console.WriteLine("Box3 的体积: {0}", volume);
         Console.ReadKey();
      }
   }
}

动态多态性

方法的重写

抽象类:不能创建实例;不能在抽象类外部声明抽象方法;通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。

在普通类中要实现方法的重写可以定义虚方法(用关键字virtual 声明),虚方法的调用是在运行时发生的。

动态多态性就是通过抽象类虚方法来实现的。

十五、命名空间(namespace)

命名空间的设计目的是提供一种让一组名称与其他名称分隔开的方式。在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。

举一个计算机系统中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。

实例:

using System;
namespace first_space
{
   class namespace_cl
   {
      public void func()
      {
         Console.WriteLine("Inside first_space");
      }
   }
}
namespace second_space
{
   class namespace_cl
   {
      public void func()
      {
         Console.WriteLine("Inside second_space");
      }
   }
}  
class TestClass
{
   static void Main(string[] args)
   {
      first_space.namespace_cl fc = new first_space.namespace_cl();
      second_space.namespace_cl sc = new second_space.namespace_cl();
      fc.func();
      sc.func();
      Console.ReadKey();
   }
}

using关键字

using 关键字表明程序使用的是给定命名空间中的名称(相当于Java中的import)。例如,我们在程序中使用 System 命名空间,其中定义了类 Console。

可以使用 using 命名空间指令,这样在使用的时候就不用在前面加上命名空间名称。该指令告诉编译器随后的代码使用了指定命名空间中的名称。

命名空间是可以嵌套的。

十六、常见的异常类

C# 异常是使用类来表示的。C# 中的异常类主要是直接或间接地派生于 System.Exception 类。System.ApplicationExceptionSystem.SystemException 类是派生于 System.Exception 类的异常类。

System.ApplicationException 类支持由应用程序生成的异常。所以程序员定义的异常都应派生自该类。

System.SystemException 类是所有预定义的系统异常的基类。

下表列出了一些派生自 Sytem.SystemException 类的预定义的异常类:
在这里插入图片描述
与java一样,用户也可以自定义异常类。

十七、C#特性(Attributed)

**特性(Attribute)**是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

特性(Attribute)用于添加元数据(用于描述数据的数据),如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。

类似于Java中的注解(@)

十八、委托(Delegate)

委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。

委托(Delegate)特别用于实现事件回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。

申明委托

委托声明决定了可由该委托引用的方法。委托可指向一个与其具有相同标签的方法。

如:

public delegate int MyDelegate (string s);

上面的委托可被用于引用任何一个带有一个单一的 string 参数的方法,并返回一个 int 类型变量。

实例化委托

一旦声明了委托类型,委托对象必须使用 new 关键字来创建,且与一个特定的方法有关。当创建委托时,传递到 new 语句的参数就像方法调用一样书写,但是不带有参数。实例:

using System;

delegate int NumberChanger(int n);
namespace DelegateAppl
{
   class TestDelegate
   {
      static int num = 10;
      public static int AddNum(int p)
      {
         num += p;
         return num;
      }

      public static int MultNum(int q)
      {
         num *= q;
         return num;
      }
      public static int getNum()
      {
         return num;
      }

      static void Main(string[] args)
      {
         // 创建委托实例
         NumberChanger nc1 = new NumberChanger(AddNum);
         NumberChanger nc2 = new NumberChanger(MultNum);
         // 使用委托对象调用方法
         nc1(25);
         Console.WriteLine("Value of Num: {0}", getNum());
         nc2(5);
         Console.WriteLine("Value of Num: {0}", getNum());
         Console.ReadKey();
      }
   }
}

委托的多播

委托对象可使用 “+” 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。

使用委托的这个有用的特点,您可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting),也叫组播。下面的程序演示了委托的多播:

using System;

namespace Test05
{
    class Program
    {
        public delegate void A(string str);
        public static void Print(string str)
        {
            Console.WriteLine(str);
        }

		// 小张类
        class MrZhang
        {
       		public static void BuyTicket()
            {
                Console.WriteLine("NND,每次都让我去买票,鸡人呀!");
            }

            public static void BuyMovieTicket()
            {
                Console.WriteLine("我去,自己泡妞,还要让我带电影票!");
            }

            public static void BuyJuice()
            {
                Console.WriteLine("太可恶啦!还叫我带果汁!");
            }
        }
        //小明类
        class MrMing
        {
            //声明一个委托,其实就是个“命令”
            public delegate void BugTicketEventHandler();

            public static void Main(string[] args)
            {
                //这里就是具体阐述这个命令是干什么的,本例是MrZhang.BuyTicket“小张买车票”
                BugTicketEventHandler myDelegate = new BugTicketEventHandler(MrZhang.BuyTicket);

                myDelegate += MrZhang.BuyMovieTicket;
                myDelegate += MrZhang.BuyJuice;
                //这时候委托被附上了具体的方法
                myDelegate();
                Console.ReadKey();
            }
        }
    }
}

十九、集合(Collection)

集合(Collection)类是专门用于数据存储和检索的类。这些类提供了对栈(stack)、队列(queue)、列表(list)和哈希表(hash table)的支持。大多数集合类实现了相同的接口。

集合的分类

下面是各种常用的 System.Collection 命名空间的类:

  1. 动态数组(ArrayList):可被单独索引的对象的有序集合。它基本上可以替代一个数组。但是,与数组不同的是,它可以使用索引在指定的位置添加和移除项目,动态数组会自动重新调整它的大小。它也允许在列表中进行动态内存分配、增加、搜索、排序各项。
  2. 哈希表(HashTable):Hashtable 类代表了一系列基于键的哈希代码组织起来的键/值对。它使用来访问集合中的元素。
  3. 排序列表(SortedList):SortedList 类代表了一系列按照键来排序的键/值对,这些键值对可以通过键和索引来访问。排序列表是数组和哈希表的组合。它包含一个可使用键或索引访问各项的列表。如果您使用索引访问各项,则它是一个动态数组(ArrayList),如果您使用键访问各项,则它是一个哈希表(Hashtable)。集合中的各项总是按键值排序。
  4. 堆栈(Stack):堆栈(Stack)代表了一个后进先出的对象集合。当您需要对各项进行后进先出的访问时,则使用堆栈。当您在列表中添加一项,称为推入元素,当您从列表中移除一项时,称为弹出元素。
  5. 队列(Queue):队列(Queue)代表了一个先进先出的对象集合。当您需要对各项进行先进先出的访问时,则使用队列。当您在列表中添加一项,称为入队,当您从列表中移除一项时,称为出队
  6. 点阵列(BitArray):它代表了一个使用值 1 和 0 来表示的二进制数组。当您需要存储位,但是事先不知道位数时,则使用点阵列。您可以使用整型索引从点阵列集合中访问各项,索引从零开始。

二十、数据结构

一、数组(Array)

数组具有以下的特点:

  1. 数组属于线性结构,在内存中是连续存放的。
  2. 数组的元素类型必须相同。
  3. 数组可以直接通过下标访问。
  4. 数组的查找速度非常快,新增和删除速度慢。
  5. 数组在初始化时要指定数组长度。

二、动态数组(ArrayList)

动态数组具有以下的特点:

  1. ArrayList的底层其实就是一个数组。
  2. ArrayList在声明时不必指定长度,会根据存储的数据动态的增加或减少长度。
  3. ArrayList会把所有的元素都当做Object处理,因此可以存储不同数据类型的元素。
  4. 插入和删除一个元素时,会移动它之后所有元素的位置,效率低,频繁进行插入或者删除元素推荐使用LinkedList。
  5. ArrayList是非类型安全的,在插入和删除元素时会进行拆箱和装箱问题,影响性能,效率低。

三、泛型List

泛型List具有以下的特点:

  1. List是ArrayList的泛型类。
  2. 泛型List需要在声明时指定具体的类型。
  3. 泛型List没有装箱和拆箱操作,因此List比ArrayList效率高而且类型安全。

四、双向链表(LinkedList)

双向链表具有如下特点:

  1. 链表的节点在内存中的空间是不连续的,每块空间称作一个节点,每个节点都存有一个前驱和后置指针,分别指向前一个节点和后一个节点,因此向链表中添加和删除元素的效果高,只需要更改相应节点的指针指向即可。
  2. 链表的查找效率低。查找元素时不能通过下标进行访问,只能从头开始通过地址按顺序查找。

五、堆栈(Stack)

堆栈具有如下特点:

  1. 堆栈是先进后出的原则,最先插入的元素最后被访问,最后插入的元素最先被访问。
  2. Push入栈,Pop出栈并返回栈顶元素,Peek只返回栈顶元素。

六、Queue(队列)

队列具有以下特点:

  1. 队列是先进先出的原则,最先进入的元素最先被访问,最后进入的元素最后被访问。
  2. Enqueue入队列,Dequeue出队列并返回列首元素,Peek只返回列首元素。

七、字典(Dictionary)

字典具有以下特点:

  1. 创建字典时需要指定key和value的数据类型。
  2. 字典中的key值是唯一的,value的值可以不唯一。
  3. 可以通过key快速查找对应的value,速度快,但是消耗内存(即读取和增删的速度都快,用空间换性能)。

几种常见数据结构的使用情景

Array需要处理的元素数量确定并且需要使用下标进行访问时可以考虑,不过建议使用List。
ArrayList不推荐使用,建议使用泛型List。
泛型List需要处理的元素数量不确定时,通常建议使用。
LinkedList链表适合元素数量不固定,而且需要经常增减节点的情况,链表增减元素效率高。
Queue队列适合于先进先出的情况。
Stack堆栈适合于先进后出的情况。
Dictionary<K,T>字典适合于需要键值对操作的情况。

详见使用场景:https://www.cnblogs.com/Dewumu/p/12067573.html

二十一、多线程

线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。

线程是轻量级进程,它是程序执行的最小单元。一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了 CPU 周期的浪费,同时提高了应用程序的效率。

线程的生命周期

线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。

下面列出了线程生命周期中的各种状态:

  • 未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。
  • 就绪状态:当线程准备好运行并等待 CPU 周期时的状况。
  • 不可运行状态:下面的几种情况下线程是不可运行的:
    • 已经调用 Sleep 方法
    • 已经调用 Wait 方法
    • 通过 I/O 操作阻塞
  • 死亡状态:当线程已完成执行或已中止时的状况。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值