首页 > 编程语言 >.NET 7 和 C# 11 的 7 大自定义扩展方法

.NET 7 和 C# 11 的 7 大自定义扩展方法

时间:2023-03-01 10:55:15浏览次数:58  
标签:11 task 自定义 C# tasks Task static null public

.NET 7 和 C# 11 的 7 大自定义扩展方法

原创2023-01-13 11:59·启辰8

 

介绍

自从我开始了解扩展方法以来,我不断地发现新的可能性,让我的编码生活更轻松。 扩展方法是 SOLID完美应用 中的O ——开闭原则。 一个类应该尽可能简单,并且只在其他组件真正需要时才将属性和方法暴露给外部。

通过扩展方法,您可以为您的类实现额外的方法,而无需更改类本身! 这非常适合将类作为参数的重复方法。

实现扩展方法非常简单。 看看下面的例子:

namespace System;

public static class EnumerableExtensions
{
    public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action) {
        foreach (var item in sequence) 
            action(item);
    }
}

这在每个 IEnumerable<T>(由 this 关键字引入)上实现了 ForEach() 方法,就像您从 List<T> 类型中了解到的那样。 要访问此方法,您唯一需要做的就是添加对相应命名空间的引用,在本例中为 System。

我最常用的 7 种扩展方法

自 .NET 6 以来,Microsoft 为 IEnumerable<T> 实现了一些扩展方法,我之前将这些方法列在了我的首选列表中,包括 DistinctBy() 和 Chunk()。 但是,从 .NET 7 开始,我仍然缺少一些非常重要的方法,尤其是处理任务(Task)集合的方法。

事不宜迟,以下是我在 .NET 7 中最常用的扩展方法:

1. TryAsync

第一种扩展方法是我最喜欢的一种。 您有多少次在刚刚创建的方法周围添加 try-catch 块,并且因为它破坏了外观而变得有点恼火? 当您的方法返回 Task 或 Task<T> 时,这是一个非常简洁的解决方案:

public static async Task<Result> TryAsync(
    this Task task,
    Action<Exception> errorHandler = null
) {
    try {
        await task;
        return Result.Ok();
    }
    catch (Exception ex) {
        if (errorHandler is not null) errorHandler(ex);
        return ex;
    }
}

public static async Task<Result<T>> TryAsync<T>(
    this Task<T> task,
    Action<Exception> errorHandler = null
) where T : class {
    try {
        return await task;
    }
    catch (Exception ex) {
        if (errorHandler is not null) errorHandler(ex);
        return ex;
    }
}

现在你可以这样写:

var result = await GetSomethingAsync().TryAsync();

您的方法将自动包装在 try-catch 块中。 此外,您可以为其他辅助逻辑(如日志记录)提供 errorHandler。 然后可以检查这些方法返回的结果是否成功,后续您可以继续您的逻辑。

 

2. WhenAllAsync

集合中有多个 Task 或 Task<T> 以正常方式处理有点不方便。 您需要调用 Task.WhenAll(tasks) 这让我有点脱离流程,因为它不是流畅的风格。 这是它的样子:

public static async Task<IEnumerable<T>> WhenAllAsync<T>(this IEnumerable<Task<T>> tasks) {
    if (tasks is null)
        throw new ArgumentNullException(nameof(tasks));

    return await Task
        .WhenAll(tasks)
        .ConfigureAwait(false);
}

public static Task WhenAllAsync(this IEnumerable<Task> tasks) {
    if (tasks is null)
        throw new ArgumentNullException(nameof(tasks));

    return Task
        .WhenAll(tasks);
}

现在我可以方便的地处理任何任务集合:

var results = await tasks.WhenAllAsync();

3. WhenAllSequentialAsync

下一个扩展方法甚至可以为您节省几行代码,并让您能够逐个执行每个任务。 这在您可能不会并行执行许多任务的情况下很有用。

public static async Task<IEnumerable<T>> WhenAllSequentialAsync<T>(this IEnumerable<Task<T>> tasks) {
    if (tasks is null)
        throw new ArgumentNullException(nameof(tasks));

    var results = new List<T>();
    foreach (var task in tasks)
        results.Add(await task.ConfigureAwait(false));
    return results;
}

public static async Task WhenAllSequentialAsync(this IEnumerable<Task> tasks) {
    if (tasks is null)
        throw new ArgumentNullException(nameof(tasks));

    foreach (var task in tasks)
        await task.ConfigureAwait(false);
}

4. WhenAllParallelAsync

最后但同样重要的是,可能有一个用例,您可以并行执行任务,但可能有最大数量限制。 对于这种情况,我有以下扩展方法:

public static async Task<IEnumerable<T>> WhenAllParallelAsync<T>(
    this IEnumerable<Task<T>> tasks,
    int degree
) {
    if (tasks is null)
        throw new ArgumentNullException(nameof(tasks));

    var results = new List<T>();
    foreach (var chunk in tasks.Chunk(degree)) {
        var chunkResults = await Task.WhenAll(chunk).ConfigureAwait(false);
        results.AddRange(chunkResults);
    }
    return results;
}

public static async Task WhenAllParallelAsync(
    this IEnumerable<Task> tasks,
    int degree
) {
    if (tasks is null)
        throw new ArgumentNullException(nameof(tasks));

    foreach (var chunk in tasks.Chunk(degree)) 
        await Task.WhenAll(chunk).ConfigureAwait(false);
}

使用 degree 参数,您可以指定应并行执行多少个任务。

5. MapAsync

这也是一个流畅的扩展,但这次是针对单个 Task 或 Task<T>。

public static async Task<TOut> MapAsync<TIn, TOut>(
    this Task<TIn> task,
    Func<TIn, Task<TOut>> mapAsync
) {
    if (task is null)
        throw new ArgumentNullException(nameof(task));

    if (mapAsync is null)
        throw new ArgumentNullException(nameof(mapAsync));

    return await mapAsync(await task);
}

public static async Task<TOut> MapAsync<TIn, TOut>(
    this Task<TIn> task,
    Func<TIn, TOut> map
) {
    if (task is null)
        throw new ArgumentNullException(nameof(task));

    if (map is null)
        throw new ArgumentNullException(nameof(map));

    return map(await task);
}

使用此扩展方法,您可以将 Task<T1> 流畅地映射到 Task<T2>,类似于 Enumerable.Select() 方法。

6. DoAsync

类似的方法是 DoAsync() ,但不是转换任务,而是可以使用任务结果执行辅助逻辑而不改变其返回值。

public static async Task<T> DoAsync<T>(
    this Task<T> task,
    Func<T, Task> tapAsync
) {
    if (task is null)
        throw new ArgumentNullException(nameof(task));

    if (tapAsync is null)
        throw new ArgumentNullException(nameof(tapAsync));

    var res = await task;
    await tapAsync(res);
    return res;
}

public static async Task<T> DoAsync<T>(
    this Task<T> task,
    Action<T> tap
) {
    if (task is null)
        throw new ArgumentNullException(nameof(task));

    if (tap is null)
        throw new ArgumentNullException(nameof(tap));

    var res = await task;
    tap(res);
    return res;
}

7. String.Join

最后一个是我有时用于连接字符串以进行日志记录的扩展方法。 通常,您可以为此使用 string.Join() ,但同样,这并不流畅,让我无法理解。

public static string Join(this IEnumerable<string> sequence, string separator = "") {
    return string.Join(separator, sequence);
}

缩略

技术上不是扩展方法,但对节省一些代码也很有用,以下是我的缩写方法:

namespace System;

public static class Abbreviations
{
    public static IEnumerable<T> Arr<T>(params T[] elements) => elements;

    public static void Try(
        Action action,
        Action<Exception>? errorHandler = null
    ) {
        try {
            action();
        }
        catch (Exception ex) {
            if (errorHandler is not null) errorHandler(ex);
        }
    }

    public static Result<T> Try<T>(
        Func<T> action,
        Action<Exception>? errorHandler = null
    ) where T : class 
    {
        try {
            return action();
        }
        catch (Exception ex) {
            if (errorHandler is not null) errorHandler(ex);
            return ex;
        }
    }
}

要轻松使用它们,您必须在根级别创建一个特殊文件(我通常将其称为 GlobalUsings.cs)并在其中放入以下行:

global using static System.Abbreviations;

1. Arr

Arr 方法是从现有值创建新 IEnumerable<T> 的缩写。 通常,这将需要非常丑陋的代码,如下所示:

var arr = new [] { param1, param2,... };

现在你可以这样写:

var arr = Arr(param1, param2,... );

哪个看起来更漂亮?

2. Try

您有多少次只是想在一个新方法周围添加一个简单的 try-catch 块,但由于它在您的代码中占用了太多空间,所以感觉很难看? 这是一个缩写方法 Try() 的解决方案。

在最短的形式中,你可以写:

Try(TestMethod);

其中 TestMethod 是一个不带任何参数的方法。 这使您能够编写极短的代码并消除那些讨厌的 try-catch 块。

对于异步方法,我建议您使用上面的扩展方法 TryAsync,因为它是流畅的风格。

当然,还有更多的可能性,您可以使用此架构并制作自己的缩写词和扩展方法。 在评论中让我知道你的想法。

我希望您发现其中一些方法有用并将它们应用到您的项目中。

谢谢阅读!

标签:11,task,自定义,C#,tasks,Task,static,null,public
From: https://www.cnblogs.com/sexintercourse/p/17167315.html

相关文章

  • 分享 29个超有用的 JavaScript 单行代码(下)
    分享29个超有用的JavaScript单行代码(下)原创2023-02-0321:19·前端达人大家好,在上一篇文章《分享29个超有用的JavaScript单行代码(上)》分享了日期、字符串、数字、数......
  • 分享29个超有用的 JavaScript 单行代码(上)
    分享29个超有用的JavaScript单行代码(上)原创2023-02-0320:13·前端达人在这篇文章中,我列出了一系列29个JavaScript单行代码,它们在使用原生JS(≥ES6)进行开发时非常......
  • echarts渲染3d地图以及交互事件
    环境vue2导入安装npm包echartsecharts-glimport*asechartsfrom"echarts"import"echarts-gl"html<divid="map-container"></div>jsimportGeoZJfr......
  • (笔记)EtherCat报文格式详解
     说明:本文是从EtherCat初学者的角度来撰写的,详细介绍的其报文格式,特别是应用层与Canopen之间的关系。特别感谢:https://zhuanlan.zhihu.com/p/406428272?utm_id=0的贡献。......
  • Ceph RGW ElasticSearch同步模块介绍
    ElasticSearch同步模块注意:截至2020年5月31日,仅支持Elasticsearch6及更低版本。不支持ElasticSearch7。此同步模块将其他区域的元数据写入ElasticSearch。......
  • 腾讯云对象存储 COS搭建个人网站
    腾讯云对象存储COS搭建个人网站,简单易操作,方便快捷。 只需要将你的网站资源上传即可,然后设置上你的自定义CDN加速域名,一个个人网站就上线啦!当然,你也可以不用设置......
  • BlackBerry面向中国市场研发多款安全数字液晶仪表盘
    BlackBerry面向中国市场研发多款安全数字液晶仪表盘2023-02-1410:24·DoNewsDoNews2月14日消息,BlackBerry近日宣布,一级汽车供应商重庆矢崎仪表有限公司(以下简称:重......
  • 7 个 高效的 JavaScript 库,总有一个适合你
    7个高效的JavaScript库,总有一个适合你原创2023-02-1409:45·前端达人转载说明:原创不易,未经授权,谢绝任何形式的转载当我们可以通过使用库轻松实现相同的结果时,为......
  • MASA MAUI Plugin (九)Android相册多选照片(使用Android Jetpack套件库)
    背景MAUI的出现,赋予了广大.Net开发者开发多平台应用的能力,MAUI是Xamarin.Forms演变而来,但是相比Xamarin性能更好,可扩展性更强,结构更简单。但是MAUI对于平台相关的实现并......
  • C. Fox And Names
    C.FoxAndNameshttps://codeforces.com/problemset/problem/510/C 思路使用拓扑序算法,找出前后的依赖的可能情况。如果拓扑序未找到所有的节点,表明存在依赖环,这种......