LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

C# 中的 Span:高效编码的最佳朋友

admin
2024年9月13日 10:5 本文热度 566

以提高 C# 代码中 Collections 和 Arrays 的性能和内存使用率(您还记得 String 是一个字符数组,加载方式略有不同,但无论如何)。我终于设法找到了一些时间来更深入地研究 System.Span。

我整理了本指南来分享我所学到的知识。它充满了实用的技巧和示例,可帮助您在自己的项目中利用 Span。如果您想优化 C# 代码,本指南是一个很好的起点!

那么,您想让 C# 代码运行得更快并更有效地使用内存吗?认识 Spans:一款方便的工具,可简化内存块的处理并帮助您的应用程序获得更好的性能。让我们深入了解 Span 的工作原理,探索实际示例,了解它们的区别,并了解如何将它们用于 JSON 解析,以及将集合与 Span 相互转换。

什么是 Span?

在 C# 中,是表示内存的连续区域的结构。它们允许您处理数据切片,而不会产生额外内存分配的开销。Span<T>ReadOnlySpan<T>

  • Span<T>:允许对内存进行读取和写入操作。

  • ReadOnlySpan<T>:用于只读操作,确保数据无法修改。

Span 对于性能关键型方案特别有用,因为它们支持直接访问数据和高效使用内存。

为什么应该关心 Span?

  1. 更快的性能:Span 有助于减少内存分配和垃圾回收压力。它们允许您直接有效地处理数据。

  2. 更安全的代码:Span 可防止缓冲区溢出等常见错误,并提供边界检查。

  3. 多功能性:它们适用于其他内存区域的数组、字符串和切片,使其适用于各种数据处理场景。

Span 是如何实现的

在后台,Span 被设计为轻量级和快速:

  • 堆栈分配:跨度通常在堆栈上分配,这样速度更快并避免堆分配。

  • 内存安全:它们确保对内存的安全访问,并内置边界检查以防止越界错误。

  • 无堆开销:与数组不同,Span 不会创建额外的堆分配,从而减少内存开销并提高性能。

和 之间的差异Span<T>ReadOnlySpan<T>

虽然两者 和 处理连续内存,但它们的用法和功能不同:Span<T>ReadOnlySpan<T>

Span<T>:

  • 可变:您可以修改 .Span<T>

  • 示例:更改数组或缓冲区中的元素。

int[] numbers = { 1, 2, 3, 4, 5 };
Span<int> span = new Span<int>(numbers);
span[0] = 10; // Modifies the original array
Console.WriteLine(numbers[0]); // Outputs 10

ReadOnlySpan<T>:

  • Immutable:您无法修改 .ReadOnlySpan<T>

  • 示例:从字符串或数组中读取值而不更改它们。

string text = "Hello, World!";
ReadOnlySpan<char> readOnlySpan = text.AsSpan();
// readOnlySpan[0] = 'h'; // This line would cause a compilation error
Console.WriteLine(readOnlySpan.ToString()); // Outputs "Hello, World!"

集合到 Span 的转换

Span 旨在与数组等集合无缝协作,从而可以轻松地在集合和 Span 之间进行转换。

从array到 Span:

int[] numbers = { 1, 2, 3, 4, 5 };
Span<int> spanFromArray = new Span<int>(numbers);

从 span 到 array:

Span<int> span = stackalloc int[] { 1, 2, 3, 4, 5 };
int[] arrayFromSpan = span.ToArray();

从 String 到 ReadOnlySpan:

string text = "Hello, World!";
ReadOnlySpan<char> spanFromString = text.AsSpan();

从 ReadOnlySpan 到 String:

ReadOnlySpan<char> span = "Hello, World!".AsSpan();
string strFromSpan = span.ToString(); // Note: Converts to a new string

集合转换的实际示例

示例:使用数组和 Span

int[] array = { 1, 2, 3, 4, 5 };
Span<int> span = array;
span[0] = 10; // Modifies the original array
Console.WriteLine(string.Join(", ", array)); // Outputs: 10, 2, 3, 4, 5

示例:将 Span 转换为数组

Span<int> span = stackalloc int[] { 10, 20, 30 };
int[] array = span.ToArray();
Console.WriteLine(string.Join(", ", array)); // Outputs: 10, 20, 30

示例:使用 ReadOnlySpan 提取子字符串

string text = "Hello, World!";
ReadOnlySpan<char> span = text.AsSpan();
ReadOnlySpan<char> helloSpan = span.Slice(0, 5);
Console.WriteLine(helloSpan.ToString()); // Outputs: Hello

实际示例:使用 Span 编写自己的 JSON 解析器

Span 对于有效处理字符串数据特别有用。所以现在让我们尝试编写我们自己的 JSON 解析器,它可以在不创建不必要的中间字符串的情况下工作。

简单的 JSON 解析器

public void ParseJson(ReadOnlySpan<char> jsonData)
{
   // Find the start of the value for a specific key
   ReadOnlySpan<char> key = "name";
   int keyStart = jsonData.IndexOf(key);
   
   if (keyStart == -1)
   {
       Console.WriteLine("Key not found");
       return;
   }
   
   // Move past the key and find the colon
   int valueStart = jsonData.Slice(keyStart + key.Length).IndexOf(':') + keyStart + key.Length + 1;
   int valueEnd = jsonData.Slice(valueStart).IndexOf(',');
   
   if (valueEnd == -1) // If no comma, this is the last value
   {
       valueEnd = jsonData.Slice(valueStart).IndexOf('}');
   }
   
   // Extract and print the value
   ReadOnlySpan<char> value = jsonData.Slice(valueStart, valueEnd);
   Console.WriteLine(value.ToString().Trim('"')); // Remove quotes
}

解析器非常适合原子数据类型,但不支持 Array 或内部 Object 等复杂类型。

因此,让我们添加一个基于 Span  Array 解析器:

public void ProcessJsonArray(ReadOnlySpan<char> jsonArray)
{
   int currentIndex = 0;
   
   while (currentIndex < jsonArray.Length)
   {
       int start = jsonArray.Slice(currentIndex).IndexOf('{');
       if (start == -1) break; // No more objects

       int end = jsonArray.Slice(currentIndex).IndexOf('}');
       if (end == -1) break; // Incomplete object
       
       ReadOnlySpan<char> jsonObject = jsonArray.Slice(currentIndex + start, end - start + 1);
       ProcessJsonObject(jsonObject);
       
       currentIndex += end + 1; // Move past the current object
   }
}

并添加嵌套对象支持:

private void ProcessJsonObject(ReadOnlySpan<char> jsonObject)
{
   // Simple key-value extraction, assuming keys and values are properly formatted
   int colonIndex = jsonObject.IndexOf(':');
   ReadOnlySpan<char> key = jsonObject.Slice(1, colonIndex - 2); // Skipping surrounding quotes
   ReadOnlySpan<char> value = jsonObject.Slice(colonIndex + 1).Trim(); // Extract value and trim
   
   Console.WriteLine($"Key: {key.ToString()}, Value: {value.ToString()}");
}

将所有内容放在一起:解析 JSON 数据

以下是结合使用上述所有函数来解析完整 JSON 字符串的方法:

public void ParseJson(ReadOnlySpan<char> jsonData)
{
   int start = 0;
   while (start < jsonData.Length)
   {
       int objectStart = jsonData.Slice(start).IndexOf('{');
       if (objectStart == -1) break;

       int objectEnd = jsonData.Slice(start).IndexOf('}');
       if (objectEnd == -1) break;

       ReadOnlySpan<char> jsonObject = jsonData.Slice(start + objectStart, objectEnd - objectStart + 1);
       ProcessJsonObject(jsonObject);

       start += objectEnd + 1;
   }
}

干的好!现在我们有了自己的内存有效型 JSON 解析器实现,终于可以忘记这些 Newtonsoft.Json nuget 包更新问题了......可能最近 3 到 4 年都没有面对它,因为 Microsoft 编写了自己的实现,但如果你面对 - 现在你知道该怎么做了!

需要注意的事项

  • 范围:Span 是堆栈分配的,应在创建它们的方法中使用。

  • 固定:在处理非托管内存时,请谨慎使用固定,因为它可能会影响垃圾回收。

  • 兼容性:确保您的开发环境支持 Span,尤其是对于较旧的框架。

Span 是 C# 中的一项强大功能,可以帮助您高效安全地管理内存。通过了解 和 之间的差异,以及如何在集合和 span 之间进行转换,您可以编写更高效、更简洁的代码。将 Span 用于任务Span<T>ReadOnlySpan<T>


该文章在 2024/9/13 10:05:31 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2024 ClickSun All Rights Reserved