C# 学习笔记
发布于 2026-04-06更新于 2026-11-148828 字53 分钟 · 阅读时间
2024-7-8-14:00~ 最后更新时间 2025-2-20
读音:C+Sharp
[ʃɑːp](夏普)
##简介:
编程的学习路径:
编写程序的步骤:
有用的资源:
C# 与 .NET 的关系:
主要包含:
后缀为.cs
using System; namespace HelloWorld { class Hello { static void Main() { Console.WriteLine("Hello,World"); Console.ReadKey(); // 防止命令行窗口一闪而过 } }}using -> 引用命名空间
如上,
using System;允许直接使用System命名空间下的Console类,而不需要完整地写出System.Console.WriteLine("Hello, World");。
更像Java
和Java差不多
所有关键字(表格):
| 保留关键字 | ||||||
| abstract | as | base | bool | break | byte | case |
| catch | char | checked | class | const | continue | decimal |
| default | delegate | do | double | else | enum | event |
| explicit | extern | false | finally | fixed | float | for |
| foreach | goto | if | implicit | in | in (generic modifier) | int |
| interface | internal | is | lock | long | namespace | new |
| null | object | operator | out | out (generic modifier) | override | params |
| private | protected | public | readonly | ref | return | sbyte |
| sealed | short | sizeof | stackalloc | static | string | struct |
| switch | this | throw | true | try | typeof | uint |
| ulong | unchecked | unsafe | ushort | using | virtual | void |
| volatile | while | |||||
| 上下文关键字 | ||||||
| add | alias | ascending | descending | dynamic | from | get |
| global | group | into | join | let | orderby | partial (type) |
| partial (method) | remove | select | set | |||
分为值类型(Value Type)、引用类型(Reference Type)和指针类型(Pointer Type)。
byte、short、int、long、sbyte、ushort、uint、ulongfloat、double、decimalcharbool| 类型 | 描述 | 范围 | 默认值 |
|---|---|---|---|
| bool | 布尔值 | True 或 False | False |
| byte | 8 位无符号整数 | 0 到 255 | 0 |
| char | 16 位 Unicode 字符 | U +0000 到 U +ffff | '\0' |
| 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 |
可空类型:Nullable<T>或T?
可空类型是一种特殊的值类型,可以存储 null 值
单问号?:Nullable<T>的简写
Nullable<T>i = 3; <==>int? i = 3;
双问号??:空合并运算符
int? i = null; int j = i ?? 3;// 如果i为空,则j为 3
int? i = null;int ii; // 默认值为0if (i.HasValue) { //`HasValue`属性:判断是否有值 Console.WriteLine(i.Value);}else { Console.WriteLine("i is null");}int j = i ?? 3;Console.WriteLine(j);object、string、dynamic对象类型:class、interface、delegate、array
当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱
int i = 123;object o = i; // 装箱int j = (int)o; // 拆箱动态类型:dynamic
dynamic类型是在编译时不知道其类型,而是在运行时确定的类型。(python 中只有动态类型,而 C#中有静态类型和动态类型)
dynamic d = 20;d = "Hello, World";字符串类型:string
字符串是引用类型,但是使用起来像值类型
string str = "Hello, World";@符号:
@符号用于定义字符串字面量,可以包含多行,转义字符会被忽略
string str = @"C:/Windows"; // == string str = "C://Windows"
@字符串中可以任意换行,但是不能有连续的两个@
string str = @"C:/WindowsC:/Program Files"; // 换行符及缩进空格都计算在字符串长度之内指针类型和 C/C++中的指针类似,但是 C#中的指针类型只能在不安全代码块(unsafe)中使用。
unsafe{ int var = 20; int* p = &var; Console.WriteLine((int)p); // 输出地址 Console.WriteLine(*p); // 输出值}数组与Java类似,
其他的与C++类似
### 类型转换- 隐式转换:从小范围类型到大范围类型(自动转换)- 显式转换:从大范围类型到小范围类型(强制转换) ```csharpint i = 10;double d = i; // 隐式转换int j = (int)d; // 显式转换Convert.To大写数据类型(变量)int i = 10;string str = Convert.ToString(i);大写数据类型.Parse(字符串)string str = "10";int i = int.Parse(str);大写数据类型.TryParse(字符串, out 变量)string str = "10";int i;if (int.TryParse(str, out i)) { Console.WriteLine(i);}else { Console.WriteLine("转换失败");}访问修饰符:
public:公有的,所有类都可以访问private:私有的,只有本类可以访问 (默认)protected:受保护的,只有本类和子类可以访问internal:内部的,只有本程序集可以访问protected internal:受保护的内部的,本类、子类和本程序集可以访问程序集:编译后的文件,包括了所有的类、接口、枚举等
class类可以继承另一个class类 (父类又叫基类,子类又叫派生类)
interface接口可以继承另一个interface接口
class A { public void Print() { Console.WriteLine("A"); }} class B : A { // 继承的语法 public void Print() { Console.WriteLine("B"); }} B b = new B();b.Print(); // 输出Bclass类不支持多继承,但是interface接口支持多继承
interface A { void Print();} interface B { void Print();} class C : A, B { public void Print() { Console.WriteLine("C"); }}using System;using System.Collections.Generic; public class Shape { public int X { get; private set; } public int Y { get; private set; } public int Height { get; set; } public int Width { get; set; } // 虚方法 public virtual void Draw() { Console.WriteLine("执行基类的画图任务"); }} class Circle : Shape{ public override void Draw() { Console.WriteLine("画一个圆形"); base.Draw(); }}class Rectangle : Shape { public override void Draw() { Console.WriteLine("画一个长方形"); base.Draw(); }}class Triangle : Shape { public override void Draw()n{ Console.WriteLine("画一个三角形"); base.Draw(); }} class Program { static void Main(string[] args) { // 创建一个 List<Shape> 对象,并向该对象添加 Circle、Triangle 和 Rectangle var shapes = new List<Shape> { new Rectangle(), new Triangle(), new Circle() }; // 使用 foreach 循环对该列表的派生类进行循环访问,并对其中的每个 Shape 对象调用 Draw 方法 foreach (var shape in shapes) { shape.Draw(); // 调用子类的Draw方法 } Console.WriteLine("按下任意键退出。"); Console.ReadKey(); } }/* 运行结果:画一个长方形执行基类的画图任务画一个三角形执行基类的画图任务画一个圆形执行基类的画图任务按下任意键退出。*/静态多态性:方法重载与运算符重载
方法重载:方法名相同,参数列表不同
不可重载的运算符:=, ?:, ::, ., is, new 等等。(但像是 C++就支持重载=运算符)
这些运算符不可重载主要是因为它们要么涉及到基础的内存操作和类型检查,要么是语言的核心特性,允许重载这些运算符可能会导致代码难以理解和维护
using System; interface IParentInterface{ void ParentInterfaceMethod();} interface IMyInterface : IParentInterface{ void MethodToImplement();} class InterfaceImplementer : IMyInterface{ static void Main() { InterfaceImplementer iImp = new InterfaceImplementer(); iImp.MethodToImplement(); iImp.ParentInterfaceMethod(); } public void MethodToImplement() { Console.WriteLine("MethodToImplement() called."); } public void ParentInterfaceMethod() { Console.WriteLine("ParentInterfaceMethod() called."); }}/** 结果输出:MethodToImplement() called.ParentInterfaceMethod() called.*/命名空间的设计目的是提供一种让一组名称与其他名称分隔开的方式。在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。
举一个计算机系统中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。
namespace namespace_name{ // 代码声明 // 命名空间可以嵌套}.来访问嵌套命名空间中的成员这是最常见的用法,例如:
using System;
using Namespace1.SubNameSpace; 2. using static 指令:指定无需指定类型名称即可访问其静态成员的类型
using static System.Math;var = PI; // 直接使用 System.Math.PI 3. 起别名
using Project = PC.MyCompany.Project; 4. using 语句:将实例与代码绑定
using (Font font3 = new Font("Arial", 10.0f),
font4 = new Font("Arial", 10.0f))
{
// Use font3 and font4.
}
代码段结束时,自动调用 font3 和 font4 的 Dispose 方法,释放实例。
#define PI = 3.14 ×| 指令 | 描述 |
|---|---|
#define | 定义一个符号,可以用于条件编译。 |
#undef | 取消定义一个符号。 |
#if | 开始一个条件编译块,如果符号被定义则包含代码块。 |
#elif | 如果前面的 #if 或 #elif 条件不满足,且当前条件满足,则包含代码块。 |
#else | 如果前面的 #if 或 #elif 条件不满足,则包含代码块。 |
#endif | 结束一个条件编译块。 |
#warning | 生成编译器警告信息。 |
#error | 生成编译器错误信息。 |
#region | 标记一段代码区域,可以在IDE中折叠和展开这段代码,便于代码的组织和阅读。 |
#endregion | 结束一个代码区域。 |
#line | 更改编译器输出中的行号和文件名,可以用于调试或生成工具的代码。 |
#pragma | 用于给编译器发送特殊指令,例如禁用或恢复特定的警告。 |
#nullable | 控制可空性上下文和注释,允许启用或禁用对可空引用类型的编译器检查。 |
" "中使用正则表达式@,以便让""中变成字面量,方便符号\的书写$int num = 123;string pattern = @$"\d+{num}";或者int num = 123;string pattern = $@"\d+{num}";验证邮箱地址:
using System.Text.RegularExpressions; string email = "example@example.com";Regex regex = new Regex(@"^\S+@\S+\.\S+$");bool isValid = regex.IsMatch(email);预定义的异常类:
| 异常类 | 描述 |
|---|---|
| System.IO.IOException | 处理 I/O 错误。 |
| System.IndexOutOfRangeException | 处理当方法指向超出范围的数组索引时生成的错误。 |
| System.ArrayTypeMismatchException | 处理当数组类型不匹配时生成的错误。 |
| System.NullReferenceException | 处理当依从一个空对象时生成的错误。 |
| System.DivideByZeroException | 处理当除以零时生成的错误。 |
| System.InvalidCastException | 处理在类型转换期间生成的错误。 |
| System.OutOfMemoryException | 处理空闲内存不足生成的错误。 |
| System.StackOverflowException | 处理栈溢出生成的错误。 |
通过try与chach块,来将核心程序语句与错误处理语句分离开
using System;namespace ErrorHandlingApplication{ class DivNumbers { int result; DivNumbers() { result = 0; } public void division(int num1, int num2) { try { result = num1 / num2; } catch (DivideByZeroException e) { Console.WriteLine("Exception caught: {0}", e); } finally { Console.WriteLine("Result: {0}", result); } } static void Main(string[] args) { DivNumbers d = new DivNumbers(); d.division(25, 0); Console.ReadKey(); } }}您也可以定义自己的异常。用户自定义的异常类是派生自 ApplicationException 类。下面的实例演示了这点:
using System;namespace UserDefinedException{ class TestTemperature { static void Main(string[] args) { Temperature temp = new Temperature(); try { temp.showTemp(); } catch(TempIsZeroException e) { Console.WriteLine("TempIsZeroException: {0}", e.Message); } Console.ReadKey(); } }}public class TempIsZeroException: ApplicationException{ public TempIsZeroException(string message): base(message) { }}public class Temperature{ int temperature = 0; public void showTemp() { if(temperature == 0) { throw (new TempIsZeroException("Zero Temperature found")); } else { Console.WriteLine("Temperature: {0}", temperature); } }}/* 执行结果:TempIsZeroException: Zero Temperature found*/如果异常是直接或间接派生自 System.Exception 类,您可以抛出一个对象。您可以在 catch 块中使用 throw 语句来抛出当前的对象,如下所示:
catch(Exception e){ ... Throw e}作用:错误通知、提供错误细节、让上层调用者处理,让自定义异常类来处理
当打开文件进行读写时,它变成一个流。
从根本上说,流是通过通信路径传递的字节序列。有两个主要的流:输入流 和 输出流*。**输入流用于从文件读取数据(读操作),输出流**用于向文件写入数据(写操作)*
放在System.IO中。
下表是一些 System.IO 命名空间中常用的非抽象类:
| I/O 类 | 描述 |
|---|---|
| BinaryReader | 从二进制流读取原始数据。 |
| BinaryWriter | 以二进制格式写入原始数据。 |
| BufferedStream | 字节流的临时存储。 |
| Directory | 有助于操作目录结构。 |
| DirectoryInfo | 用于对目录执行操作。 |
| DriveInfo | 提供驱动器的信息。 |
| File | 有助于处理文件。 |
| FileInfo | 用于对文件执行操作。 |
| FileStream | 用于文件中任何位置的读写。 |
| MemoryStream | 用于随机访问存储在内存中的数据流。 |
| Path | 对路径信息执行操作。 |
| StreamReader | 用于从字节流中读取字符。 |
| StreamWriter | 用于向一个流中写入字符。 |
| StringReader | 用于读取字符串缓冲区。 |
| StringWriter | 用于写入字符串缓冲区。 |
System.IO 命名空间中的 FileStream 类有助于文件的读写与关闭。该类派生自抽象类 Stream。
创建一个 FileStream 对象:
FileStream 对象名 = new FileStream("文件路径", 文件打开时的操作, 文件访问方法, 文档关闭时的操作);FileStream F = new FileStream("sample.txt", FileMode.Open, FileAccess.Read, FileShare.Read);| 参数 | 描述 |
|---|---|
| FileMode | FileMode 枚举定义了各种打开文件的方法。FileMode 枚举的成员有:
|
| FileAccess | FileAccess 枚举的成员有:Read、ReadWrite 和 Write。 |
| FileShare | FileShare 枚举的成员有:
|
using System;using System.IO; namespace FileIOApplication{ class Program { static void Main(string[] args) { FileStream F = new FileStream("test.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite); for (int i = 1; i <= 20; i++) { F.WriteByte((byte)i); } F.Position = 0; for (int i = 0; i <= 20; i++) { Console.Write(F.ReadByte() + " "); } F.Close(); Console.ReadKey(); } }}/* 输出:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -1*/当然还有其他高级方法用于操作文档,但这里不在赘述
语法:
[特性名称(必要参数,可选参数=值,...)]三种预定义特性:
[AttributeUsage(AttributeTargets.All)][Conditional("DEBUG")][Obsolete("该方法已过时", true)]步骤:
System.Attributeusing System; [AttributeUsage(AttributeTargets.All)]public class HelpAttribute : Attribute // 继承自Attribute{ public readonly string Url; // 只读的,只能在构造函数中修改 private string topic; public string Topic { get { return topic; } set { topic = value; } } public HelpAttribute(string url) { this.Url = url; }} [HelpAttribute("Information on the class MyClass")] // 通过将特性放在类的前面,将特性应用于类class MyClass{} class Program{ static void Main() { System.Reflection.MemberInfo info = typeof(MyClass); // 获取类的信息 object[] attributes = info.GetCustomAttributes(true); // 获取类的特性 for (int i = 0; i < attributes.Length; i++) { System.Console.WriteLine(attributes[i]); // 输出特性 } }}get与set,它两又称访问器get就是只读属性等等public class Person{ private string name; public string Name { get { return name; } set { name = value; } }}public class Person{ public string Name { get; set; }}通过这串代码,可以让编译器自动创建默认的这两个属性
也可以通过 public、private、protected 等访问修饰符来控制属性的访问性,决定谁可以访问和修改属性的值。
索引器的语法与属性类似,但是使用 this 关键字定义。可以有一个或多个形参,
public class MyClass{ private string[] data = new string[10]; // 索引器的定义 public string this[int index] { get { return data[index]; } set { data[index] = value; } }}MyClass 类定义了一个索引器,可以通过整数索引来访问 data 数组中的元素。
MyClass obj = new MyClass();obj[0] = "First element";string element = obj[0];```通过 obj[0] 访问了 MyClass 对象中的索引器,但实际上是调用了定义在类中的 get 和 set 方法通过多个参数来支持多维访问
public class Matrix{ private int[,] data = new int[3, 3]; public int this[int row, int column] { get { return data[row, column]; } set { data[row, column] = value; } }} Matrix matrix = new Matrix();matrix[0, 0] = 1;int value = matrix[0, 0];public、private、protected 等访问修饰符。get 方法),也可以是可读写的(包含 get 和 set 方法)。readonly 修饰符,以提供只读的索引器。定义包含两个部分:声明与实例化
// 委托声明public delegate void MyDelegate(string message); // 方法符合委托的签名public static void Method1(string message){ Console.WriteLine(message);} public static void Method2(string message){ Console.WriteLine(message.ToUpper());} // 创建委托实例MyDelegate delegate1 = Method1;MyDelegate delegate2 = Method2; // 调用委托delegate1("Hello, world!");delegate2("Goodbye, world!");
MyDelegate是一个委托类型,它可以引用任何接受一个string参数且没有返回值的方法。delegate1和delegate2是MyDelegate类型的委托实例,它们分别引用了Method1和Method2方法。当调用delegate1("Hello, world!")时,实际上会调用Method1("Hello, world!")
+ 和 - 运算符,可以将多个委托实例合并成一个多播委托。多播委托按顺序调用其包含的方法。BeginInvoke 和 EndInvoke 方法执行异步操作时。event 关键字修饰。+= 运算符订阅事件,而使用 -= 运算符取消订阅。using System; public class Publisher // 发布者{ // 声明一个事件,使用 EventHandler 委托 public event EventHandler<MyEventArgs> MyEvent; // 通过调用 OnMyEvent 来触发事件,并将事件数据 MyEventArgs 传递给订阅者 public void RaiseEvent() { Console.WriteLine("触发事件了!"); OnMyEvent(new MyEventArgs("事件数据")); } protected virtual void OnMyEvent(MyEventArgs e) { MyEvent?.Invoke(this, e); }} public class Subscriber // 订阅者{ // 通过 Subscribe 方法订阅了 Publisher 的事件 public void Subscribe(Publisher pub) { // 订阅事件 pub.MyEvent += HandleEvent; } public void Unsubscribe(Publisher pub) { // 取消订阅事件 pub.MyEvent -= HandleEvent; } // 封装事件的相关数据 // 通过 HandleEvent 方法来处理事件 private void HandleEvent(object sender, MyEventArgs e) { Console.WriteLine($"事件处理结果: {e.Message}"); }} public class MyEventArgs : EventArgs{ public string Message { get; } public MyEventArgs(string message) { Message = message; }} public class Program{ public static void Main() { // 创建了一个发布者与两个订阅者 Publisher publisher = new Publisher(); Subscriber subscriber1 = new Subscriber(); Subscriber subscriber2 = new Subscriber(); // 订阅事件 subscriber1.Subscribe(publisher); subscriber2.Subscribe(publisher); // 触发事件 // 此时,两个订阅者都知道了改事件 publisher.RaiseEvent(); // 取消订阅事件(可选) subscriber1.Unsubscribe(publisher); subscriber2.Unsubscribe(publisher); // 再次触发事件 publisher.RaiseEvent(); }}/* 输出:触发事件了!事件处理结果:事件数据事件处理结果:事件数据触发事件了!*/作用是允许您编写可以在多种数据类型上重用的代码,而不需要为每种数据类型编写重复的代码。这种重用性提高了代码的灵活性和可维护性
public class GenericList<T> // 创建了一个泛型类{ private T[] data; private int index; public GenericList(int capacity) { data = new T[capacity]; index = 0; } public void Add(T item) { if (index < data.Length) { data[index] = item; index++; } else { throw new IndexOutOfRangeException("超出列表容量"); } } public T Get(int index) { if (index >= 0 && index < this.index) { return data[index]; } else { throw new IndexOutOfRangeException("索引超出范围"); } }} public class Program{ public static void Main() { // 创建一个存储整数的泛型列表 GenericList<int> intList = new GenericList<int>(5); intList.Add(10); intList.Add(20); Console.WriteLine("列表intList中的元素"); Console.WriteLine(intList.Get(0)); Console.WriteLine(intList.Get(1)); // 创建一个存储字符串的泛型列表 GenericList<string> stringList = new GenericList<string>(3); stringList.Add("Hello"); stringList.Add("World"); Console.WriteLine("\n列表stringList中的元素"); Console.WriteLine(stringList.Get(0)); Console.WriteLine(stringList.Get(1)); }}特点:
delegate 来定义。delegate 关键字定义一个匿名方法,例如: delegate (参数们) { /* 方法体 */ };using System; public class Program{ public static void Main() { // 使用匿名方法创建委托 Action<string> printMessage = delegate(string message) { Console.WriteLine("Message: " + message); }; // 调用委托 printMessage("Hello, world!"); }}特点:
=> 符号分隔两部分:左边是参数列表,右边是方法体。(parameters) => expression 或 (parameters) => { statements }。using System; public class Program{ public static void Main() { // Lambda 表达式创建委托 Action<string> printMessage = message => Console.WriteLine("Message: " + message); // 调用委托 printMessage("Hello, world!"); }}总之:Lambda 表达式在作为匿名函数方面,比原匿名函数更加简介与直观
在计算机编程中,"不安全代码"(Unsafe Code)通常指的是在编程语言中允许直接访问计算机内存的特性或语法。这种代码允许开发人员绕过编译器的某些安全检查,以获得更高的灵活性和性能,但也带来了潜在的安全风险和错误处理挑战。
指针是值为另一个变量的地址的变量,即,内存位置的直接地址。就像其他变量或常量,您必须在使用指针存储其他变量地址之前声明指针。
| 实例 | 描述 |
|---|---|
int* p |
p 是指向整数的指针。 |
double* p |
p 是指向双精度数的指针。 |
float* p |
p 是指向浮点数的指针。 |
int** p |
p 是指向整数的指针的指针。 |
int*[] p |
p 是指向整数的指针的一维数组。 |
char* p |
p 是指向字符的指针。 |
void* p |
p 是指向未知类型的指针。 |
int* p1, p2, p3; // 正确int *p1, *p2, *p3; // 错误using System;namespace UnsafeCodeApplication{ class Program { static unsafe void Main(string[] args) { int var = 20; int* p = &var; Console.WriteLine("Data is: {0} ", var); Console.WriteLine("Address is: {0}", (int)p); Console.ReadKey(); } }}如果您使用的是 Visual Studio IDE,那么您需要在项目属性中启用不安全代码。
步骤如下:
使用指针检索数据
using System;namespace UnsafeCodeApplication{ class Program { public static void Main() { unsafe { int var = 20; int* p = &var; Console.WriteLine("Data is: {0} " , var); Console.WriteLine("Data is: {0} " , p->ToString()); Console.WriteLine("Address is: {0} " , (int)p); } Console.ReadKey(); } }}传递指针作为方法的参数
using System;namespace UnsafeCodeApplication{ class TestPointer { public unsafe void swap(int* p, int *q) { int temp = *p; *p = *q; *q = temp; } public unsafe static void Main() { TestPointer p = new TestPointer(); int var1 = 10; int var2 = 20; int* x = &var1; int* y = &var2; Console.WriteLine("Before Swap: var1:{0}, var2: {1}", var1, var2); p.swap(x, y); Console.WriteLine("After Swap: var1:{0}, var2: {1}", var1, var2); Console.ReadKey(); } }}使用指针访问数组元素
using System;namespace UnsafeCodeApplication{ class TestPointer { public unsafe static void Main() { int[] list = {10, 100, 200}; fixed(int *ptr = list) /* 显示指针中数组地址 */ for ( int i = 0; i < 3; i++) { Console.WriteLine("Address of list[{0}]={1}",i,(int)(ptr + i)); Console.WriteLine("Value of list[{0}]={1}", i, *(ptr + i)); } Console.ReadKey(); } }}线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。
线程是轻量级进程。一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了 CPU 周期的浪费,同时提高了应用程序的效率。
到目前为止我们编写的程序是一个单线程作为应用程序的运行实例的单一的过程运行的。但是,这样子应用程序同时只能执行一个任务。为了同时执行多个任务,它可以被划分为更小的线程。
using System;using System.Threading; namespace MultithreadingApplication{ class MainThreadProgram { static void Main(string[] args) { Thread th = Thread.CurrentThread; th.Name = "MainThread"; Console.WriteLine("This is {0}", th.Name); Console.ReadKey(); } }}/* 输出:This is MainThread*/using System;using System.Threading; namespace MultithreadingApplication{ class ThreadCreationProgram { public static void CallToChildThread() { Console.WriteLine("子线程启动"); } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("在 Main: 创建子线程"); Thread childThread = new Thread(childref); childThread.Start(); Console.ReadKey(); } }}/* 输出:在 Main: 创建子线程子线程启动*/下面的实例演示了 sleep() 方法的使用,用于在一个特定的时间暂停线程
using System;using System.Threading; namespace MultithreadingApplication{ class ThreadCreationProgram { public static void CallToChildThread() { Console.WriteLine("子线程启动"); // 线程暂停 5000 毫秒 int sleepfor = 5000; Console.WriteLine("子线程暂停 {0} 秒", sleepfor / 1000); Thread.Sleep(sleepfor); Console.WriteLine("子线程终止"); } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("在 Main: 创建子线程"); Thread childThread = new Thread(childref); childThread.Start(); Console.ReadKey(); } }}/* 输出:在 Main: 创建子线程子线程启动子线程暂停 5 秒子线程终止*/using System;using System.Threading; namespace MultithreadingApplication{ class ThreadCreationProgram { public static void CallToChildThread() { Console.WriteLine("子线程启动"); for (int i = 0; i <= 5; i++) { Console.WriteLine(i); } Console.WriteLine("子线程终止"); } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("在 Main: 创建子线程"); Thread childThread = new Thread(childref); childThread.Start(); childThread.Join(); Console.WriteLine("子线程终止"); Console.ReadKey(); } }}/* 输出:在 Main: 创建子线程子线程启动012345子线程终止子线程终止*/using System;using System.Threading; namespace MultithreadingApplication{ class ThreadCreationProgram { public static void CallToChildThread() { Console.WriteLine("子线程启动"); } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("在 Main: 创建子线程"); Thread childThread = new Thread(childref); childThread.Start(); childThread.Priority = ThreadPriority.Highest; Console.ReadKey(); } }}/* 输出:在 Main: 创建子线程子线程启动*/using System;using System.Threading; namespace MultithreadingApplication{ class ThreadCreationProgram { public static void CallToChildThread() { int sleepfor = 5000; Console.WriteLine("子线程启动"); Console.WriteLine("子线程暂停 {0} 秒", sleepfor / 1000); Thread.Sleep(sleepfor); Console.WriteLine("子线程终止"); } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("在 Main: 创建子线程"); Thread childThread = new Thread(childref); childThread.IsBackground = true; childThread.Start(); Console.WriteLine("主线程终止"); } }}/* 输出:在 Main: 创建子线程主线程终止子线程启动子线程暂停 5 秒*/using System;using System.Threading; namespace MultithreadingApplication{ class ThreadPoolExample { public static void CallToChildThread(object state) { Console.WriteLine("线程池线程开始: {0}", state); } static void Main(string[] args) { for (int i = 0; i <= 5; i++) { ThreadPool.QueueUserWorkItem(new WaitCallback(CallToChildThread), i); } Console.ReadKey(); } }}/* 输出:线程池线程开始: 0线程池线程开始: 1线程池线程开始: 2线程池线程开始: 3线程池线程开始: 4线程池线程开始: 5*/using System;using System.Threading; namespace MultithreadingApplication{ class ThreadSynchronization { public static int flag = 1; public static int flag2 = 1; public static object lock1 = new object(); public static object lock2 = new object(); public static void function1() { lock (lock1) { Console.WriteLine("function1: 锁住 lock1"); Thread.Sleep(3000); lock (lock2) { Console.WriteLine("function1: 锁住 lock2"); } } } public static void function2() { lock (lock2) { Console.WriteLine("function2: 锁住 lock2"); Thread.Sleep(3000); lock (lock1) { Console.WriteLine("function2: 锁住 lock1"); } } } static void Main(string[] args) { Thread t1 = new Thread(function1); Thread t2 = new Thread(function2); t1.Start(); t2.Start(); } }}/* 输出:function1: 锁住 lock1function2: 锁住 lock2*/using System;using System.Threading; namespace MultithreadingApplication{ class ThreadSynchronization { public static int flag = 1; public static int flag2 = 1; public static object lock1 = new object(); public static object lock2 = new object(); public static void function1() { lock (lock1) { Console.WriteLine("function1: 锁住 lock1"); Thread.Sleep(3000); lock (lock2) { Console.WriteLine("function1: 锁住 lock2"); } } } public static void function2() { lock (lock2) { Console.WriteLine("function2: 锁住 lock2"); Thread.Sleep(3000); lock (lock1) { Console.WriteLine("function2: 锁住 lock1"); } } } static void Main(string[] args) { Thread t1 = new Thread(function1); Thread t2 = new Thread(function2); t1.Start(); t2.Start(); } }}/* 输出:function1: 锁住 lock1function2: 锁住 lock2*/using System;using System.Collections.Concurrent;using System.Threading; namespace MultithreadingApplication{ class ThreadSafeCollection { public static ConcurrentQueue<string> queue = new ConcurrentQueue<string>(); public static void Producer() { for (int i = 0; i < 5; i++) { queue.Enqueue("产品 " + i); Console.WriteLine("生产者: 产品 " + i); Thread.Sleep(1000); } } public static void Consumer() { string result; for (int i = 0; i < 5; i++) { queue.TryDequeue(out result); Console.WriteLine("消费者: " + result); Thread.Sleep(1000); } } static void Main(string[] args) { Thread producer = new Thread(Producer); Thread consumer = new Thread(Consumer); producer.Start(); consumer.Start(); } }}/* 输出:生产者: 产品 0消费者: 产品 0生产者: 产品 1消费者: 产品 1生产者: 产品 2消费者: 产品 2生产者: 产品 3消费者: 产品 3生产者: 产品 4消费者: 产品 4*/using System;using System.Threading; namespace MultithreadingApplication{ class ThreadCreationProgram { public static void CallToChildThread() { try { Console.WriteLine("子线程开始"); for (int counter = 0; counter <= 10; counter++) { Thread.Sleep(500); Console.WriteLine(counter); } Console.WriteLine("子线程结束"); } catch (ThreadAbortException e) { Console.WriteLine("线程终止异常"); } finally { Console.WriteLine("无法捕获线程异常"); } } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("在 Main: 创建子线程"); Thread childThread = new Thread(childref); childThread.Start(); // 停止主线程一段时间 Thread.Sleep(2000); // 现在中止子线程 Console.WriteLine("在 Main: 终止子线程"); childThread.Abort(); Console.ReadKey(); } }}/* 输出:在 Main: 创建子线程子线程开始012345678910子线程结束在 Main: 终止子线程线程终止异常无法捕获线程异常*/分享你的想法,与大家交流讨论
Twikoo 加载失败
Twikoo 脚本加载失败