C#如何安全、高效地玩转任何种类的内存之Span的本质

yizhihongxing

C#如何安全、高效地玩转任何种类的内存之Span的本质攻略

什么是Span

Span<T> 是 .NET Core 中新增的一种类型,它是一种类似于指针的结构体,表示一块连续的内存区域,其内容不一定是类型T的连续区域,可以是其他原语类型(如byte、int、long等)的连续区间。Span<T> 可以让我们高效地访问和读写内存在不进行垃圾回收的情况下,同时提高代码的可读性和安全性。

为什么要使用Span

在.NET Core中,Span<T>可以取代一部分App端耗费运行性能的,无法直接内联为CPU指令的Array类型(如byte[]、char[]等)。在某些场景下,使用Span比使用数组更快、更节省内存、更安全且易于维护。大多数情况下,Span可以完全替代传统Array类型。Span能够提供更快的数据访问速度,而且由于它并不在堆上分配内存,不会引发GC和堆内碎片。

Span的核心API

Span支持各种常见的API,比如基于范围(“range-based”)的访问、数组初始化、复制、遍历等等。以下是Span类型的几个重要API方法:

Slice

Slice方法可以创建一个新的Span,其所占据的连续内存段为当前原始Span所指定的一部分。从而可以通过该方法在Span中提供对子范围的非常高效的访问。

Span<byte> original = stackalloc byte[10];
original[0] = 1;
original[9] = 10;
Span<byte> slice = original.Slice(2, 6);
Console.WriteLine(slice[0]); // output is 3

Pin

针对临时的内存分配,Span还提供了一种手段——Pin,用于将Span关联起来的内存固定在内存中,防止GC在处理对象的过程中重复移动这个对象所提供的内存。如果你不经意间将 Span 的内存地址传递给其他代码,GC 稍后可能会移动该内存,导致应用程序崩溃。因此,通过固定内存而使其不可移动,即可避免该问题。

unsafe
{
    fixed (int* p = &source[0])
    fixed (int* q = &dest[0])
    {
        var src = new Span<int>(p, source.Length);
        var dst = new Span<int>(q, dest.Length);
        Span<int> slice = src.Slice(1, 2);
        MemoryExtensions.CopyTo(slice, dst);
    }
}

TryRead, TryWrite

TryRead和TryWrite方法能够更加高效地处理Span的访问。TryRead可以通过返回success值来判断是否成功读取,而不是抛出异常。TryWrite可以通过返回success值来判断是否成功写入,同时返回写入字节数。

Span<byte> buffer = new byte[4] { 1, 2, 3, 4 };
Span<int> casted = MemoryMarshal.Cast<byte, int>(buffer);

int val;
if(casted.TryRead(out val))
{
    Console.WriteLine(val);
}

Span的示例

数组快速排序

下面展示了使用Span实现的静态快速排序算法,其中排序的数组是int类型的。

public static void QuickSort(Span<int> span)
{
    if (span.Length <= 1)
    {
        return;
    }

    int pivotIndex = span.Length / 2;
    int pivotValue = span[pivotIndex];

    int left = 0;
    int right = span.Length - 1;

    while (left <= right)
    {
        while (span[left] < pivotValue)
        {
            left++;
        }

        while (span[right] > pivotValue)
        {
            right--;
        }

        if (left <= right)
        {
            int temp = span[left];
            span[left] = span[right];
            span[right] = temp;
            left++;
            right--;
        }
    }

    QuickSort(span.Slice(0, left));
    QuickSort(span.Slice(left));
}

比较两个Span

下面用举例的方式来展示比较两个Span的方法。

public static bool SequenceEqual(Span<byte> span1, Span<byte> span2)
{
    if (span1.Length != span2.Length)
    {
        return false;
    }

    for (int i = 0; i < span1.Length; i++)
    {
        if (span1[i] != span2[i])
        {
            return false;
        }
    }

    return true;
}

总结

Span是一个非常有用的数据类型,使用它有助于提高应用程序的性能和可读性。使用Span可以避免访问托管内存时的内存拷贝,提高程序的效率。Span API有助于简化代码,从而提高类型安全性。通过实践,Span也可以成为您的.NET Core开发工具箱中的一部分。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#如何安全、高效地玩转任何种类的内存之Span的本质 - Python技术站

(0)
上一篇 2023年5月15日
下一篇 2023年5月15日

相关文章

  • C#常用的命名规则汇总

    C#常用的命名规则汇总 在C#中,良好的命名规则对于开发者而言是至关重要的。它可以明确描述代码所实现的功能,提高代码的可读性和可维护性。在本文中,我们将介绍C#中常用的命名规则。 Pascal命名法 Pascal命名法指的是将每个单词的首字母大写的命名方式。比如,定义一个表示学生姓名的变量,可以使用Pascal命名法: string StudentName;…

    C# 2023年5月15日
    00
  • C# List的用法小结

    针对题目“C# List的用法小结”的完整攻略,以下是我整理的详细讲解: 1. C# List 简介 C# List是.NET Framework中的一个通用集合类,可以存储一组对象,并且可根据索引值访问列表中的元素,支持添加、删除、排序等操作。List类是泛型类,其中“T”代表一个类型参数,表示列表中存储的元素的数据类型。由于是泛型类,因此可以根据不同的需…

    C# 2023年5月31日
    00
  • C#简单实现表达式目录树(Expression)

    下面是我对于“C#简单实现表达式目录树(Expression)”的完整攻略。 表达式目录树简介 表达式目录树是一个树形结构,可以用来表示 LINQ 查询和 Lambda 表达式。表达式目录树将表达式树转换为运行时的委托,从而实现了高效的查询。在表达式目录树中,每个节点都表示一个表达式,可以是参数、常量、变量、运算符等。表达式目录树不仅能用于构建查询,还可以用…

    C# 2023年5月31日
    00
  • c# 遍历获取所有文件的示例代码

    针对“c# 遍历获取所有文件的示例代码”的完整攻略,我将通过以下几个步骤详细说明。 1. 确定遍历目标 在编写代码之前,需要先明确需要遍历的目标文件夹。可以通过以下方式获取目标文件夹路径,此处以桌面为例: string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desk…

    C# 2023年5月31日
    00
  • WPF创建Prism应用程序

    WPF 创建 Prism 应用程序的完整攻略 Prism 是一个用于构建 XAML 应用程序的开源框架,它提供了一组工具和库,帮助开发人员构建可扩展、可重用和易于维护的应用程序。本攻略将介绍如何使用 Prism 创建 WPF 应用程序,并提供两个示例说明。 步骤 步骤1:创建 WPF 应用程序 首先,我们需要创建一个 WPF 应用程序。可以使用 Visual…

    C# 2023年5月17日
    00
  • C#使用Thrift作为RPC框架入门详细教程

    C#使用Thrift作为RPC框架入门详细教程 什么是Thrift Thrift是一个由Facebook开源的高效、多语言支持的远程过程调用(RPC)框架,可用于构建跨平台、可扩展的服务。 安装Thrift 在使用Thrift之前,先需要安装Thrift的编译器(thrift.exe),可以从Thrift官网(https://thrift.apache.or…

    C# 2023年6月1日
    00
  • ASP.NET Core基础之Startup类

    ASP.NET Core 基础之 Startup 类 Startup 类是 ASP.NET Core 应用程序的入口点,它负责配置应用程序的服务和中间件。本攻略将介绍 ASP.NET Core 中 Startup 类的基础知识和用法。 Startup 类的作用 Startup 类的主要作用是配置应用程序的服务和中间件。在 Startup 类中,我们可以注册服…

    C# 2023年5月17日
    00
  • ASP.NET C#生成下拉列表树实现代码

    下面我将详细讲解“ASP.NET C#生成下拉列表树实现代码”的完整攻略。 1. 什么是下拉列表树? 下拉列表树,顾名思义就是下拉列表和树结构的结合体。通俗点说,就是在下拉列表的每一项展开后,可以看到类似树形结构的多级列表。下面是一个简单的下拉列表树的示例: – 第一项 + 子项 1 + 子项 2 – 第二项 + 子项 1 + 子项 2 2. 实现下拉列表树…

    C# 2023年5月31日
    00
合作推广
合作推广
分享本页
返回顶部