百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

DotNet9学习指南(中篇:高级特性与语言增强)

itomcoil 2025-02-17 12:30 14 浏览

一、委托、事件和 Lambda 表达式

1.1 委托的定义、使用和组合

1.1.1 委托的定义

委托是一种引用类型,它可以封装一个或多个方法,并且可以像调用方法一样调用委托。委托的定义语法如下:

delegate 返回类型 委托名(参数列表);

例如,定义一个委托用于封装一个接受两个整数并返回整数的方法:

delegate int MathOperation(int a, int b);

1.1.2 委托的使用

定义好委托后,就可以使用它来封装具体的方法。以下是一个完整的示例:

using System;

delegate int MathOperation(int a, int b);

class Program
{
    static int Add(int a, int b)
    {
        return a + b;
    }

    static int Subtract(int a, int b)
    {
        return a - b;
    }

    static void Main()
    {
        // 创建委托实例并关联 Add 方法
        MathOperation operation = Add;
        int result = operation(5, 3);
        Console.WriteLine($"5 + 3 = {result}");

        // 重新关联 Subtract 方法
        operation = Subtract;
        result = operation(5, 3);
        Console.WriteLine($"5 - 3 = {result}");
    }
}

1.1.3 委托的组合

委托支持多播,即一个委托实例可以封装多个方法。使用 += 运算符可以将多个方法添加到委托实例中,使用 -= 运算符可以移除某个方法。示例如下:

using System;

delegate void MessagePrinter();

class Program
{
    static void PrintHello()
    {
        Console.WriteLine("Hello");
    }

    static void PrintWorld()
    {
        Console.WriteLine("World");
    }

    static void Main()
    {
        // 创建委托实例并组合多个方法
        MessagePrinter printer = PrintHello;
        printer += PrintWorld;

        // 调用委托,依次执行封装的方法
        printer();

        // 移除一个方法
        printer -= PrintHello;
        printer();
    }
}

1.2 事件的声明、订阅和发布

1.2.1 事件的声明

事件是基于委托的一种特殊类型,用于实现对象间的消息传递。事件的声明通常使用 event 关键字,结合委托类型。示例如下:

using System;

// 定义事件委托
public delegate void EventHandler();

public class Publisher
{
    // 声明事件
    public event EventHandler MyEvent;

    public void RaiseEvent()
    {
        // 触发事件
        MyEvent?.Invoke();
    }
}

1.2.2 事件的订阅和发布

事件的订阅是指将一个方法关联到事件上,发布则是触发事件。以下是一个完整的示例:

using System;

// 定义事件委托
public delegate void EventHandler();

public class Publisher
{
    // 声明事件
    public event EventHandler MyEvent;

    public void RaiseEvent()
    {
        // 触发事件
        MyEvent?.Invoke();
    }
}

public class Subscriber
{
    public void HandleEvent()
    {
        Console.WriteLine("Event handled.");
    }
}

class Program
{
    static void Main()
    {
        Publisher publisher = new Publisher();
        Subscriber subscriber = new Subscriber();

        // 订阅事件
        publisher.MyEvent += subscriber.HandleEvent;

        // 发布事件
        publisher.RaiseEvent();
    }
}

1.3 Lambda 表达式和匿名方法

1.3.1 Lambda 表达式的语法

Lambda 表达式是一种简洁的创建匿名方法的方式。其基本语法为:

(参数列表) => 表达式或语句块

例如,使用 Lambda 表达式实现一个简单的加法操作:

using System;

class Program
{
    static void Main()
    {
        Func add = (a, b) => a + b;
        int result = add(5, 3);
        Console.WriteLine($"5 + 3 = {result}");
    }
}

1.3.2 Lambda 表达式的应用场景

Lambda 表达式常用于 LINQ 查询、事件处理等场景。例如,在 LINQ 查询中筛选集合元素:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        List numbers = new List { 1, 2, 3, 4, 5 };
        var evenNumbers = numbers.Where(n => n % 2 == 0);

        foreach (var number in evenNumbers)
        {
            Console.WriteLine(number);
        }
    }
}

1.3.3 匿名方法

匿名方法是在 C# 早期版本中用于创建临时方法的方式,Lambda 表达式是其更简洁的替代方案。示例如下:

using System;

delegate int MathOperation(int a, int b);

class Program
{
    static void Main()
    {
        MathOperation operation = delegate (int a, int b)
        {
            return a + b;
        };

        int result = operation(5, 3);
        Console.WriteLine($"5 + 3 = {result}");
    }
}

二、泛型

2.1 泛型类、泛型方法和泛型接口

2.1.1 泛型类

泛型类允许在类的定义中使用类型参数,从而可以在实例化类时指定具体的类型。示例如下:

using System;

// 定义泛型类
public class GenericList
{
    private T[] items = new T[10];
    private int count = 0;

    public void Add(T item)
    {
        if (count < items.Length)
        {
            items[count++] = item;
        }
    }

    public T GetItem(int index)
    {
        if (index < count)
        {
            return items[index];
        }
        throw new IndexOutOfRangeException();
    }
}

class Program
{
    static void Main()
    {
        // 实例化泛型类,指定类型为 int
        GenericList intList = new GenericList();
        intList.Add(5);
        int item = intList.GetItem(0);
        Console.WriteLine(item);

        // 实例化泛型类,指定类型为 string
        GenericList stringList = new GenericList();
        stringList.Add("Hello");
        string str = stringList.GetItem(0);
        Console.WriteLine(str);
    }
}

2.1.2 泛型方法

泛型方法允许在方法的定义中使用类型参数。示例如下:

using System;

class Program
{
    static T Max(T a, T b) where T : IComparable
    {
        return a.CompareTo(b) >= 0 ? a : b;
    }

    static void Main()
    {
        int maxInt = Max(5, 3);
        Console.WriteLine($"Max of 5 and 3: {maxInt}");

        string maxString = Max("apple", "banana");
        Console.WriteLine($"Max of 'apple' and 'banana': {maxString}");
    }
}

2.1.3 泛型接口

泛型接口允许在接口的定义中使用类型参数。示例如下:

using System;

// 定义泛型接口
public interface IGenericInterface
{
    T GetValue();
    void SetValue(T value);
}

// 实现泛型接口
public class GenericClass : IGenericInterface
{
    private T value;

    public T GetValue()
    {
        return value;
    }

    public void SetValue(T value)
    {
        this.value = value;
    }
}

class Program
{
    static void Main()
    {
        IGenericInterface genericInt = new GenericClass();
        genericInt.SetValue(5);
        int result = genericInt.GetValue();
        Console.WriteLine(result);
    }
}

2.2 泛型约束

泛型约束用于限制泛型类型参数的范围,确保在泛型代码中可以安全地使用类型参数的成员。常见的泛型约束有以下几种:

2.2.1where T : struct

约束类型参数必须是值类型。示例如下:

using System;

class Program
{
    static void PrintValue(T value) where T : struct
    {
        Console.WriteLine(value);
    }

    static void Main()
    {
        int number = 5;
        PrintValue(number);

        // 以下代码会编译错误,因为 string 不是值类型
        // string str = "Hello";
        // PrintValue(str);
    }
}

2.2.2where T : class

约束类型参数必须是引用类型。示例如下:

using System;

class Program
{
    static void PrintObject(T obj) where T : class
    {
        if (obj != null)
        {
            Console.WriteLine(obj.ToString());
        }
    }

    static void Main()
    {
        string str = "Hello";
        PrintObject(str);

        // 以下代码会编译错误,因为 int 不是引用类型
        // int number = 5;
        // PrintObject(number);
    }
}

2.2.3where T : new()

约束类型参数必须有一个无参构造函数。示例如下:

using System;

class Program
{
    static T CreateInstance() where T : new()
    {
        return new T();
    }

    static void Main()
    {
        var instance = CreateInstance();
        Console.WriteLine(instance.GetType().Name);
    }
}

class MyClass
{
    public MyClass()
    {
    }
}

2.2.4where T : 基类名或where T : 接口名

约束类型参数必须是指定基类的派生类或实现了指定接口。示例如下:

using System;

public interface IMyInterface
{
    void DoSomething();
}

public class MyClass : IMyInterface
{
    public void DoSomething()
    {
        Console.WriteLine("Doing something...");
    }
}

class Program
{
    static void CallDoSomething(T obj) where T : IMyInterface
    {
        obj.DoSomething();
    }

    static void Main()
    {
        MyClass myClass = new MyClass();
        CallDoSomething(myClass);
    }
}

三、异步编程

3.1 异步编程模型

异步编程是一种编程范式,用于处理可能会阻塞线程的操作,如网络请求、文件读写等,以提高程序的响应性和性能。在 .NET 中,异步编程主要通过 async 和 await 关键字来实现。

3.2Task和ValueTask

3.2.1Task

Task 是 .NET 中用于表示异步操作的类。可以使用 Task.Run 方法来启动一个异步操作。示例如下:

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        // 启动一个异步操作
        Task task = Task.Run(() =>
        {
            // 模拟耗时操作
            System.Threading.Thread.Sleep(2000);
            return 42;
        });

        // 等待任务完成并获取结果
        int result = await task;
        Console.WriteLine($"Result: {result}");
    }
}

3.2.2ValueTask

ValueTask 是 .NET Core 2.1 引入的一种轻量级的异步操作表示方式,它是一个结构体。ValueTask 适用于那些大多数情况下可以同步完成的异步操作,以减少内存分配。示例如下:

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        ValueTask valueTask = GetValueAsync();
        int result = await valueTask;
        Console.WriteLine($"Result: {result}");
    }

    static ValueTask GetValueAsync()
    {
        // 模拟可以同步完成的操作
        return new ValueTask(42);
    }
}

3.3 异步流

在 .NET 9 中,异步流得到了进一步的支持,主要通过 IAsyncEnumerable 和 await foreach 来实现。异步流用于处理异步生成的数据流。示例如下:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        await foreach (var number in GenerateNumbersAsync())
        {
            Console.WriteLine(number);
        }
    }

    static async IAsyncEnumerable GenerateNumbersAsync()
    {
        for (int i = 0; i < 5; i++)
        {
            // 模拟异步操作
            await Task.Delay(1000);
            yield return i;
        }
    }
}

四、.NET 9 新语言特性

4.1 模式匹配增强

在 .NET 9 中,模式匹配得到了进一步的增强,例如增强的类型模式和递归模式。

4.1.1 增强的类型模式

可以更方便地进行类型匹配和属性提取。示例如下:

using System;

class Program
{
    static void PrintInfo(object obj)
    {
        if (obj is string str)
        {
            Console.WriteLine($"String length: {str.Length}");
        }
        else if (obj is int num)
        {
            Console.WriteLine($"Number: {num}");
        }
    }

    static void Main()
    {
        PrintInfo("Hello");
        PrintInfo(42);
    }
}

4.1.2 递归模式

可以使用递归模式来匹配嵌套的数据结构。示例如下:

using System;

record Person(string Name, Person? Child);

class Program
{
    static void PrintChildNames(Person person)
    {
        if (person is { Child: { Name: var childName } })
        {
            Console.WriteLine($"Child name: {childName}");
            PrintChildNames(person.Child);
        }
    }

    static void Main()
    {
        var person = new Person("Alice", new Person("Bob", new Person("Charlie", null)));
        PrintChildNames(person);
    }
}

4.2 可空引用类型改进

.NET 9 对可空引用类型进行了进一步的改进,使得在处理可能为 null 的引用类型时更加安全。可以通过在项目文件中设置 enable 来启用可空引用类型检查。示例如下:

#nullable enable
using System;

class Program
{
    static void Main()
    {
        string? nullableString = null;
        if (nullableString is not null)
        {
            Console.WriteLine(nullableString.Length);
        }
    }
}

4.3 其他新特性

除了上述特性外,.NET 9 还引入了一些其他值得关注的语言特性,如记录类型的增强、静态抽象成员等。

4.3.1 记录类型的增强

记录类型在 .NET 9 中得到了进一步的优化,例如支持更灵活的构造函数和属性初始化。示例如下:

using System;

// 记录类型
record Person(string FirstName, string LastName)
{
    public string FullName => $"{FirstName} {LastName}";
}

class Program
{
    static void Main()
    {
        var person = new Person("John", "Doe");
        Console.WriteLine(person.FullName);
    }
}

4.3.2 静态抽象成员

静态抽象成员允许在接口中定义静态的抽象方法,要求实现接口的类型必须提供具体的实现。示例如下:

using System;

// 定义包含静态抽象成员的接口
public interface IStaticAbstract
{
    static abstract int GetValue();
}

// 实现接口
public class MyClass : IStaticAbstract
{
    public static int GetValue()
    {
        return 42;
    }
}

class Program
{
    static void Main()
    {
        int value = MyClass.GetValue();
        Console.WriteLine($"Value: {value}");
    }
}

总结

通过本篇的学习,你深入了解了 C# 的高级特性,包括委托、事件、Lambda 表达式、泛型、异步编程以及 .NET 9 带来的语言增强功能。这些知识能够帮助你编写更加灵活、高效和可维护的代码。

委托和事件为对象间的消息传递和回调机制提供了强大的支持,使得代码的解耦性得到提升。Lambda 表达式则以简洁的语法简化了委托的使用,尤其在 LINQ 查询等场景中发挥着重要作用。

泛型的引入让代码具有更好的复用性和类型安全性,通过泛型类、方法和接口,你可以编写出通用的代码逻辑,适用于不同的数据类型。同时,泛型约束进一步增强了泛型的灵活性和可靠性。

异步编程是现代软件开发中不可或缺的一部分,特别是在处理 I/O 密集型操作时,能够显著提高程序的性能和响应能力。Task 和 ValueTask 提供了不同场景下的异步操作解决方案,而异步流则让处理异步数据流变得更加方便。

.NET 9 的新语言特性,如模式匹配增强、可空引用类型改进、记录类型增强和静态抽象成员等,进一步提升了 C# 语言的表达能力和开发效率,使你能够更加优雅地解决复杂的编程问题。

在接下来的下篇中,我们将通过实际项目开发,综合运用这些知识,同时学习代码性能优化的技巧,进一步提升你的 .NET 9 开发能力。

实践案例:简单的异步文件读取与处理系统

下面我们通过一个简单的实践案例,综合运用本篇所学的异步编程、委托等知识,实现一个异步文件读取与处理系统。

using System;
using System.IO;
using System.Threading.Tasks;

// 定义一个委托,用于处理读取到的文件内容
delegate void FileContentProcessor(string content);

class FileProcessor
{
    // 异步读取文件内容的方法
    public async Task ReadFileAsync(string filePath, FileContentProcessor processor)
    {
        try
        {
            // 异步读取文件内容
            string content = await File.ReadAllTextAsync(filePath);
            // 调用委托处理文件内容
            processor(content);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error reading file: {ex.Message}");
        }
    }
}

class Program
{
    static async Task Main()
    {
        string filePath = "test.txt";
        FileProcessor fileProcessor = new FileProcessor();

        // 定义处理文件内容的方法
        FileContentProcessor processor = (content) =>
        {
            Console.WriteLine($"File content length: {content.Length}");
            // 这里可以添加更多的处理逻辑,例如统计单词数量等
        };

        // 异步读取文件并处理内容
        await fileProcessor.ReadFileAsync(filePath, processor);
    }
}

代码解释:

  1. 委托定义:FileContentProcessor 委托用于封装处理文件内容的方法,它接受一个 string 类型的参数,表示读取到的文件内容。
  2. FileProcessor 类:该类包含一个异步方法 ReadFileAsync,用于异步读取指定路径的文件内容。读取完成后,调用传入的委托来处理文件内容。
  3. Main 方法:在 Main 方法中,我们创建了 FileProcessor 类的实例,并定义了一个 Lambda 表达式作为 FileContentProcessor 委托的实现。然后调用 ReadFileAsync 方法异步读取文件并处理内容。

练习题

为了帮助你巩固所学知识,以下是一些练习题:

委托与事件相关

  1. 定义一个委托 Calculator,用于封装两个整数的计算操作(如加法、减法等),并编写一个方法,该方法接受两个整数和一个 Calculator 委托实例,返回计算结果。
  2. 创建一个 Publisher 类,该类包含一个事件 DataChanged,并提供一个方法 ChangeData 用于触发该事件。创建一个 Subscriber 类,该类订阅 Publisher 类的 DataChanged 事件,并在事件处理方法中输出一条消息。

泛型相关

  1. 实现一个泛型栈类 GenericStack,包含 Push、Pop 和 Peek 方法,分别用于入栈、出栈和查看栈顶元素。
  2. 编写一个泛型方法 Swap,用于交换两个变量的值。

异步编程相关

  1. 编写一个异步方法 DownloadFileAsync,用于模拟从网络下载文件的操作,下载完成后返回文件的大小(以字节为单位)。
  2. 使用 IAsyncEnumerable 实现一个异步生成斐波那契数列的方法,并在 Main 方法中使用 await foreach 遍历该数列。

.NET 9 新特性相关

  1. 使用模式匹配增强功能,编写一个方法,根据传入的对象类型进行不同的处理。如果是 string 类型,输出其长度;如果是 int 类型,输出其平方;如果是其他类型,输出默认消息。
  2. 定义一个包含静态抽象成员的接口 IMathOperation,该接口包含一个静态抽象方法 Calculate,然后实现一个类 AddOperation 实现该接口,并提供 Calculate 方法的具体实现,用于计算两个整数的和。

通过完成这些练习题,你可以更加深入地理解和掌握本篇所学的知识,为后续的学习和实践打下坚实的基础。

相关推荐

Excel新函数TEXTSPLIT太强大了,轻松搞定数据拆分!

我是【桃大喵学习记】,欢迎大家关注哟~,每天为你分享职场办公软件使用技巧干货!最近我把WPS软件升级到了版本号:12.1.0.15990的最新版本,最版本已经支持文本拆分函数TEXTSPLIT了,并...

Excel超强数据拆分函数TEXTSPLIT,从入门到精通!

我是【桃大喵学习记】,欢迎大家关注哟~,每天为你分享职场办公软件使用技巧干货!今天跟大家分享的是Excel超强数据拆分函数TEXTSPLIT,带你从入门到精通!TEXTSPLIT函数真是太强大了,轻松...

看完就会用的C++17特性总结(c++11常用新特性)

作者:taoklin,腾讯WXG后台开发一、简单特性1.namespace嵌套C++17使我们可以更加简洁使用命名空间:2.std::variant升级版的C语言Union在C++17之前,通...

plsql字符串分割浅谈(plsql字符集设置)

工作之中遇到的小问题,在此抛出问题,并给出解决方法。一方面是为了给自己留下深刻印象,另一方面给遇到相似问题的同学一个解决思路。如若其中有写的不好或者不对的地方也请不加不吝赐教,集思广益,共同进步。遇到...

javascript如何分割字符串(javascript切割字符串)

javascript如何分割字符串在JavaScript中,您可以使用字符串的`split()`方法来将一个字符串分割成一个数组。`split()`方法接收一个参数,这个参数指定了分割字符串的方式。如...

TextSplit函数的使用方法(入门+进阶+高级共八种用法10个公式)

在Excel和WPS新增的几十个函数中,如果按实用性+功能性排名,textsplit排第二,无函数敢排第一。因为它不仅使用简单,而且解决了以前用超复杂公式才能搞定的难题。今天小编用10个公式,让你彻底...

Python字符串split()方法使用技巧

在Python中,字符串操作可谓是基础且关键的技能,而今天咱们要重点攻克的“堡垒”——split()方法,它能将看似浑然一体的字符串,按照我们的需求进行拆分,极大地便利了数据处理与文本解析工作。基本语...

go语言中字符串常用的系统函数(golang 字符串)

最近由于工作比较忙,视频有段时间没有更新了,在这里跟大家说声抱歉了,我尽快抽些时间整理下视频今天就发一篇关于go语言的基础知识吧!我这我工作中用到的一些常用函数,汇总出来分享给大家,希望对...

无规律文本拆分,这些函数你得会(没有分隔符没规律数据拆分)

今天文章来源于表格学员训练营群内答疑,混合文本拆分。其实拆分不难,只要规则明确就好办。就怕规则不清晰,或者规则太多。那真是,Oh,mygod.如上图所示进行拆分,文字表达实在是有点难,所以小熊变身灵...

Python之文本解析:字符串格式化的逆操作?

引言前面的文章中,提到了关于Python中字符串中的相关操作,更多地涉及到了字符串的格式化,有些地方也称为字符串插值操作,本质上,就是把多个字符串拼接在一起,以固定的格式呈现。关于字符串的操作,其实还...

忘记【分列】吧,TEXTSPLIT拆分文本好用100倍

函数TEXTSPLIT的作用是:按分隔符将字符串拆分为行或列。仅ExcelM365版本可用。基本应用将A2单元格内容按逗号拆分。=TEXTSPLIT(A2,",")第二参数设置为逗号...

Excel365版本新函数TEXTSPLIT,专攻文本拆分

Excel中字符串的处理,拆分和合并是比较常见的需求。合并,当前最好用的函数非TEXTJOIN不可。拆分,Office365于2022年3月更新了一个专业函数:TEXTSPLIT语法参数:【...

站长在线Python精讲使用正则表达式的split()方法分割字符串详解

欢迎你来到站长在线的站长学堂学习Python知识,本文学习的是《在Python中使用正则表达式的split()方法分割字符串详解》。使用正则表达式分割字符串在Python中使用正则表达式的split(...

Java中字符串分割的方法(java字符串切割方法)

技术背景在Java编程中,经常需要对字符串进行分割操作,例如将一个包含多个信息的字符串按照特定的分隔符拆分成多个子字符串。常见的应用场景包括解析CSV文件、处理网络请求参数等。实现步骤1.使用Str...

因为一个函数strtok踩坑,我被老工程师无情嘲笑了

在用C/C++实现字符串切割中,strtok函数经常用到,其主要作用是按照给定的字符集分隔字符串,并返回各子字符串。但是实际上,可不止有strtok(),还有strtok、strtok_s、strto...