.Net Standard (标准)
.NET Standard 是微软为统一不同 .NET 平台(如 .NET Framework、.NET Core、Xamarin、Mono 等)的 API 兼容性 而制定的规范。它定义了一套标准的 基础类库 (BCL) API,确保开发者编写的代码能够在支持该标准的任何 .NET 实现中运行。
.NET Standard它是一套标准或规范,它的基础类库中只是定义一些公共被使用到的类和方法,但是这些方法并没有具体的实现代码。而 .NET Framework和.NET Core等可以遵循这套标准对.NET Standard里面类中方法进行了具体的实现。
Typeof(FileStream).Assembly.Location
我们可以去创建.NET Standard 类库或.NET Framework和.NET Core应用程序,调用FileStream文件流这个类,会发现他们都处于不同路径下程序集dll中。
.NET Standard 版本关系:
.NET Standard | 1.0 | 1.1 | 1.2 | 1.3 | 1.4 | 1.5 | 1.6 | 2.0 | 2.1 |
.NET Core | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 2.0 | 3.0 |
.NET Framework | 4.5 | 4.5 | 4.5.1 | 4.6 | 4.6.1 | 4.6.1 | 4.6.1 | 4.6.1 | 不支持 |
.NET Framework 支持.NET Standard版本只到2.0版本官方就不在更新支持。
开发工具:
- .NET CLI:命令行
- Visual Studio:IDE软件 windows-Only (新手推荐)
- Visual Studio for Mac:苹果电脑版本的IDE开发软件。
- VS Code:轻量级开发软件(跨平台)
- Jetbrains Rider: 收费IDE开发软件
使用.NET CLI 和VS Code需要去下载安装 .NET SDK 和 运行时。
.NET SDK (软件开发工具包)是使用 C# 生成和运行应用程序所需的工具和库的免费开源集合。
官方下载网址:https://dotnet.microsoft.com/zh-cn/download
安装好后,查看dotnet版本:
dotnet - -version
当前文件夹下创建控制台项目:
dotnet new console
运行:
dotnet run
创建 Web 应用项目
打开cmd命令行界面,然后输入以下命令:
dotnet new webapp --output aspnetcoreapp --no-https
上述命令在名为 aspnetcoreapp
的目录中创建新的 Web 应用项目。 该项目不需要使用 HTTPS。
运行应用
运行以下命令:
cd aspnetcoreapp
dotnet run
run
命令生成的输出如以下示例所示:
输出
Building...
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5109
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: C:\aspnetcoreapp
打开浏览器并转到输出中显示的 URL。 在本示例中,URL 为 http://localhost:5109
。
创建项目文件说明
创建控制台项目或应用程序项目目录中都会有xxx.csproj文件和xxx.cs文件
xxx.csproj 是项目文件,里面记录了这个项目使用的框架版本信息等。
如果创建的是.Net Framework项目,xxx.csproj中会记录关于整个项目的相关信息,如:项目中有那些源代码.cs文件,引用了那些程序集等。只要在项目中添加的文件都会把相关信息记录到这个文件中。
.net core创建的项目xxx.csproj文件中记录,目标版本信息,以及不属于项目中的文件信息。
xxx.cs 是程序源代码文件
项目发布
在项目上右键-》选择“发布”
选择把项目发布到哪里去?
对发布项目进行配置,对框架,发布模式等,如果勾选生成单文件系统会把依赖包和运行程序打包成一个或少量的程序文件。如果勾选了“ReadyToRun” 生成文件会变大编译时间会长一些,但是运行程序速度会有体升。
NuGet
官方网址:https://www.nuget.org/
NuGet 是微软搭建的 .NET 的包管理器, NuGet 库是所有包作者和使用者使用的中央包存储库。
在该存储库中可以搜索下载别人上传的项目文件。以及下载项目中所依赖的相关包文件。
Nuget包安装:
1.搜索需要的安装包名称,2.选择所需要的包,3.选择需要加入到那个项目中,4.选择需要的版本,5安装.
第二种使用命令方式:
1.首先在NuGet网站上去找到需要的包,找到页面中的CLI 或是Package Manager选项
CLI是在cmd窗口中执行
> dotnet add package PdfSharpCore --version 1.3.67
Package Manager是在VS中的程序包管理器控制台执行命令
复制下载命令行代码:
粘贴到控制台回车执行
卸载包文件使用:UnInstall-Package 包名称 -Version 版本号
还有一种不正规卸载方式:双击项目会打开project,删除<ItemGroup>标签中需要卸载的包<PackageReference Include="xxxxx" Version="xxxx.xx.xx">
更新包命令使用:
Update-Package 包名称 -Version 版本号
异步async 和 await 等待
异步方法使用
用async关键字修饰的方法被称为异步方法。通常异步方法返回值是泛型Task<T> ,T是真正的返回值类型。
如果异步方法没有返回值最好是把返回值定义为非泛型Task。
调用泛型方法时,一般在方法前面加await关键字,这样得到的返回值就是泛型指定的 T 类型。
异步方法具有‘传染性’,一个方法中如果使用了 await 关键字调用,那么这个方法也必须用async进行修饰。
namespace ConsoleApp1
{
internal class Program
{
static async Task Main(string[] args)
{
string filePath = @"G:\桌面\temp\新建文件夹\1.txt";
for (int i = 0; i < 1000; i++) {
WriteFile(filePath, "Hello, World!");
}
string str = await ReadFile(filePath);
Console.WriteLine(str);
Console.ReadLine();
}
/// <summary>
/// 向文件中写入字符
/// </summary>
/// <param name="fileName"></param>
/// <param name="strText"></param>
private static void WriteFile(String fileName,String strText) {
File.AppendAllTextAsync(fileName,strText);
}
/// <summary>
/// 读文件内容
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
private static async Task<String> ReadFile(String fileName) {
Task<String> str = File.ReadAllTextAsync(fileName);
String strContent = await str; //这里等于把泛型中的类型获取出来
return strContent;
//在进行包装成Task<T>泛型返回,等同于执行了Task.FromResult(strContent);
}
}
}
以上代码中 使用到了await 关键字,所以方法上必须使用async进行修饰,如果返回的是泛型使用await获取泛型中的类型。
不用await
如果我们不想使用await等待返回结果,方法上就不需要添加async
有返回值的使用Task类中的属性Resul 没有返回值类型的调用Wait()
注意:使用这种方式可能会有几率出现 死锁
/// <summary>
/// 读文件内容
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
private static String ReadFile(String fileName) {
//这里使用了Taxk的属性Result,方法上就可以不用添加async
String str = File.ReadAllTextAsync(fileName).Result;
return str;
}
/// <summary>
/// 向文件中写入字符
/// </summary>
/// <param name="fileName"></param>
/// <param name="strText"></param>
private static void WriteFile(String fileName,String strText) {
File.AppendAllTextAsync(fileName,strText).Wait();
}
如果返回值类型就是Task<T>,可以直接返回。
/// <summary>
/// 读文件内容
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
private static Task<String> ReadFile(String fileName) {
return File.ReadAllTextAsync(fileName);
}
线程暂停
Thread.Sleep(); 这个Sleep它会阻塞调用(当前)的线程。
await Task.Delay(); 不会阻塞当前线程(推荐)。
CancellationToken结构体---任务取消
在 File.ReadAllTextAsync(fileName).Wait(TimeSpan timeout, CancellationToken cancellationToken)调用了Wait方法中就使用到了CancellationToken 参数。
当线程想要被中途提前终止执行。
//结构体中静态成员变量
public static CancellationToken None => default;
//是否取消
public bool IsCancellationRequested => _source != null && _source.IsCancellationRequested;
//注册取消监听
public CancellationTokenRegistration Register(Action callback){}
//如果认为被取消,抛出异常
private void ThrowOperationCanceledException() =>
throw new OperationCanceledException(SR.OperationCanceled, this);
CancellationTokenSource类
方法:
CreateLinkedTokenSource() //创建得到CancellationToken对象
CancelAfter() //超时后发出取消信号
Cancel() //发出取消信号
Token //属性得到CancellationToken对象
例如:下载超过5秒,发出终止信号
static async Task Main(string[] args)
{
string url = @"https://www.baidu.com";
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(5000); //超时5秒,发出取消
CancellationToken cancellationToken = cts.Token; //使用CancellationTokenSource对象中的属性得到CancellationToken
await DownLoadFile(url,100, cancellationToken);
Console.ReadLine();
}
/// <summary>
/// 下载网页超过5秒
/// </summary>
/// <param name="url"></param>
/// <param name="num"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private static async Task DownLoadFile(string url,int num, CancellationToken cancellationToken) {
using (HttpClient http = new HttpClient())
{
for (int i = 0; i < num; i++)
{
string content = await http.GetStringAsync(url);
Console.WriteLine(content);
Console.WriteLine(@"*******************"+i+"****************************");
if (cancellationToken.IsCancellationRequested) {
Console.WriteLine("请求超时");
break;
// cancellationToken.ThrowIfCancellationRequested(); //调用这个会抛出异常
}
}
}
}
Task类中重要方法:
Task
是一个表示 异步操作 的类,它是 .NET 中异步编程模型(Task-based Asynchronous Pattern, TAP)的核心类型,旨在简化多线程、异步操作的开发。
Task
的核心作用
-
表示异步操作:封装一个可能需要长时间运行的操作(如 I/O、网络请求、计算等),避免阻塞主线程。
-
支持
async/await
:与async
和await
关键字配合,简化异步代码的编写。 -
任务组合与协调:支持多个任务的并行、串行执行或结果聚合(如
Task.WhenAll()
、Task.WhenAny()
)。 -
取消与进度报告:通过
CancellationToken
和IProgress<T>
支持任务取消和进度监控。
Task
的关键特性
特性 | 说明 |
---|---|
异步性 | 操作不会阻塞调用线程,资源利用更高效。 |
状态管理 | 提供 Status 属性(如 Created 、Running 、Completed 、Faulted )。 |
结果获取 | 通过 Result 属性(同步阻塞)或 await (异步非阻塞)获取结果。 |
异常处理 | 异常会被包装在 AggregateException 中,可通过 try/catch 捕获。 |
延续任务 | 使用 ContinueWith 定义任务完成后的后续操作。 |
Task
与 Thread
的区别
对比项 | Task | Thread |
---|---|---|
资源开销 | 基于线程池,复用线程,开销小。 | 直接创建线程,开销大。 |
异步支持 | 原生支持 async/await ,代码更简洁。 | 需手动管理异步逻辑。 |
结果返回 | 支持返回值(Task<TResult> )。 | 无直接返回值机制。 |
异常处理 | 异常可通过 await 捕获。 | 异常需在线程内处理。 |
任务调度 | 由 TaskScheduler 管理,灵活性高。 | 需手动调度。 |
Task<Task> WhenAny(params Task[] tasks)方法
它一个静态方法,返回值是Task<Task>泛型,
作用:任何一个Task完成,Task就完成。
public static Task<Task> WhenAny(params Task[] tasks)
{
ArgumentNullException.ThrowIfNull(tasks);
return WhenAny((ReadOnlySpan<Task>)tasks);
}
Task<TResult[]> WhenAll<TResult>(params Task<TResult>[] tasks)方法
它一个静态方法,返回值是Task<TResult[]>泛型,
作用:所有的Task完成,Task才能完成。需要等待个任务执行结束,才能结束。但是他们没有执行顺序。
示例:
static async Task Main(string[] args)
{
string filePath = @"G:\桌面\temp\新建文件夹";
Task<string> t1= File.ReadAllTextAsync(filePath + @"\1.txt");
Task<string> t2= File.ReadAllTextAsync(filePath + @"\2.txt");
string[] str = await Task.WhenAll(t1, t2); //在这里await等待全部的Task,t1,t2执行结束后在往下执行
Console.WriteLine(str[0]);
Console.WriteLine(str[1]);
Console.ReadLine();
}
Task<TResult> FromResult<TResult>(TResult result)
它一个静态,不安全的方法
作用:将普通数值转换成Task对象
例如:
String strContent ="xxxxxxxx";
Task<String> task = Task.FromResult(strContent);//在进行包装成Task<T>泛型
yield return 语法
在 C# 中,yield return
是一个用于简化迭代器(Iterator)实现的关键字组合。它允许你按需生成序列中的元素,而无需一次性构建整个集合。这种机制在延迟执行(Deferred Execution)场景中非常有用,尤其是在处理大数据或无限序列时。
核心概念
-
迭代器模式:提供一种方法顺序访问集合对象的元素,而无需暴露其底层表示。
-
延迟执行:数据按需生成,而不是预先计算所有结果。
-
状态机:编译器会将使用
yield return
的方法转换为一个状态机类,自动跟踪迭代的当前位置。
static void Main(string[] args)
{
foreach (string arg in GetStr()) {
Console.WriteLine(arg);
}
Console.ReadLine();
}
private static IEnumerable<string> GetStr() {
yield return "hello wrold";
yield return "55555555";
yield return "bababbabab";
}
关键特性
(1) 按需生成元素
-
每次调用
MoveNext()
时,代码执行到下一个yield return
处暂停。 -
元素仅在遍历时生成,减少内存占用。
(2) 支持无限序列
(3)返回值类型
-
必须返回
IEnumerable
、IEnumerable<T>
、IEnumerator
或IEnumerator<T>
。
支持无限序列
IEnumerable<int> InfiniteSequence() {
int i = 0;
while (true) {
yield return i++;
}
}
// 使用示例
foreach (var num in InfiniteSequence().Take(5)) {
Console.WriteLine(num); // 输出 0,1,2,3,4
}
支持 yield break
IEnumerable<int> EarlyTerminate(int max) {
for (int i = 0; ; i++) {
if (i >= max) yield break; // 终止迭代
yield return i;
}
}
IEnumerable<T>作为参数时可以传递数组或列表
List<> 继承 IList ,Array 继承IList,IList继承IEnumerable。
static void Main(string[] args)
{
//数组和列表都继承IList,IList继承IEnumerable,IEnumerable可以接收int数组对象
IEnumerable<int> source= new int[] { 18, 28, 63 };
foreach (int arg in FilterEvenNumbers(source)) {
Console.WriteLine(arg);
}
Console.ReadLine();
}
static IEnumerable<int> FilterEvenNumbers(IEnumerable<int> source)
{
foreach (var num in source)
{
if (num % 2 == 0)
{
yield return num; // 按需过滤
}
}
}
使用场景
处理大型数据集
static void Main(string[] args)
{
//IEnumerable<int> source= new int[] { 18, 28, 63 };
foreach (string arg in ReadLargeFile(@"G:\桌面\5555.txt"))
{
Console.WriteLine(arg);
}
Console.ReadLine();
}
static IEnumerable<string> ReadLargeFile(string path)
{
using var reader = new StreamReader(path);
while (!reader.EndOfStream) //是否读取到流末尾
{
yield return reader.ReadLine(); // 逐行读取,避免内存爆炸
}
}