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

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

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

一、委托、事件和 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 方法的具体实现,用于计算两个整数的和。

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

相关推荐

Python Qt GUI设计:将UI文件转换Python文件三种妙招(基础篇—2)

在开始本文之前提醒各位朋友,Python记得安装PyQt5库文件,Python语言功能很强,但是Python自带的GUI开发库Tkinter功能很弱,难以开发出专业的GUI。好在Python语言的开放...

Connect 2.0来了,还有Nuke和Maya新集成

ftrackConnect2.0现在可以下载了--重新设计的桌面应用程序,使用户能够将ftrackStudio与创意应用程序集成,发布资产等。这个新版本的发布中还有两个Nuke和Maya新集成,...

Magicgui:不会GUI编程也能轻松构建Python GUI应用

什么是MagicguiMagicgui是一个Python库,它允许开发者仅凭简单的类型注解就能快速构建图形用户界面(GUI)应用程序。这个库基于Napari项目,利用了Python的强大类型系统,使得...

Python入坑系列:桌面GUI开发之Pyside6

阅读本章之后,你可以掌握这些内容:Pyside6的SignalsandSlots、Envents的作用,如何使用?PySide6的Window、DialogsandAlerts、Widgets...

Python入坑系列-一起认识Pyside6 designer可拖拽桌面GUI

通过本文章,你可以了解一下内容:如何安装和使用Pyside6designerdesigner有哪些的特性通过designer如何转成python代码以前以为Pyside6designer需要在下载...

pyside2的基础界面(pyside2显示图片)

今天我们来学习pyside2的基础界面没有安装过pyside2的小伙伴可以看主页代码效果...

Python GUI开发:打包PySide2应用(python 打包pyc)

之前的文章我们介绍了怎么使用PySide2来开发一个简单PythonGUI应用。这次我们来将上次完成的代码打包。我们使用pyinstaller。注意,pyinstaller默认会将所有安装的pack...

使用PySide2做窗体,到底是怎么个事?看这个能不能搞懂

PySide2是Qt框架的Python绑定,允许你使用Python创建功能强大的跨平台GUI应用程序。PySide2的基本使用方法:安装PySide2pipinstallPy...

pycharm中conda解释器无法配置(pycharm安装的解释器不能用)

之前用的好好的pycharm正常配置解释器突然不能用了?可以显示有这个环境然后确认后可以conda正在配置解释器,但是进度条结束后还是不成功!!试过了pycharm重启,pycharm重装,anaco...

Conda使用指南:从基础操作到Llama-Factory大模型微调环境搭建

Conda虚拟环境在Linux下的全面使用指南:从基础操作到Llama-Factory大模型微调环境搭建在当今的AI开发与数据分析领域,conda虚拟环境已成为Linux系统下管理项目依赖的标配工具。...

Python操作系统资源管理与监控(python调用资源管理器)

在现代计算环境中,对操作系统资源的有效管理和监控是确保应用程序性能和系统稳定性的关键。Python凭借其丰富的标准库和第三方扩展,提供了强大的工具来实现这一目标。本文将探讨Python在操作系统资源管...

本地部署开源版Manus+DeepSeek创建自己的AI智能体

1、下载安装Anaconda,设置conda环境变量,并使用conda创建python3.12虚拟环境。2、从OpenManus仓库下载代码,并安装需要的依赖。3、使用Ollama加载本地DeepSe...

一文教会你,搭建AI模型训练与微调环境,包学会的!

一、硬件要求显卡配置:需要Nvidia显卡,至少配备8G显存,且专用显存与共享显存之和需大于20G。二、环境搭建步骤1.设置文件存储路径非系统盘存储:建议将非安装版的环境文件均存放在非系统盘(如E盘...

使用scikit-learn为PyTorch 模型进行超参数网格搜索

scikit-learn是Python中最好的机器学习库,而PyTorch又为我们构建模型提供了方便的操作,能否将它们的优点整合起来呢?在本文中,我们将介绍如何使用scikit-learn中的网格搜...

如何Keras自动编码器给极端罕见事件分类

全文共7940字,预计学习时长30分钟或更长本文将以一家造纸厂的生产为例,介绍如何使用自动编码器构建罕见事件分类器。现实生活中罕见事件的数据集:背景1.什么是极端罕见事件?在罕见事件问题中,数据集是...