【问题标题】:c# convert array type to another array type without copying and iteratingc#将数组类型转换为另一种数组类型,无需复制和迭代
【发布时间】:2021-04-07 08:47:23
【问题描述】:

我正在尝试将存储为 uint[] 的 UTF-32 编码字符串解码为 string 类型。

为此,我想使用System.Text.Encoding.UTF32.GetString 方法。但是,此函数的重载采用 Byte[] 或 Byte*。我不介意在这种特殊情况下使用 unsafe (但是来自 C,我不知道这样做的后果)

我想出了一种方法,将整个数组直接或间接复制到另一个byte[] 类型的数组。在这个问题中,我只想知道是否可以将uint[] 转换为byte[](或byte*,如果不能转换为byte[])无需复制。

【问题讨论】:

    标签: c# casting


    【解决方案1】:

    如果您使用 .NET Core 2.1+ / .NET Standard 2.1+,您可以使用 MemoryMarshal.AsBytes 在您的阵列上获取 Span<byte>,然后有一个 Encoding.GetString overload 需要 ReadOnlySpan<byte>。

    Span<byte> bytes = MemoryMarshal.AsBytes(uintArray.AsSpan());
    string str = Encoding.UTF32.GetString(bytes);
    

    我相信您已经知道,请确保您的机器的字节序与您正在使用的 UTF32Encoding 实例的字节序匹配!例如

    var encoding = new UTF32Encoding(bigEndian: !BitConverter.IsLittleEndian, byteOrderMark: false);
    

    【讨论】:

    • Span&lt;T&gt; 仅在 .NET Framework >= 5.0 中可用,我没有,我不想向用户推送,我希望有 .NET Framework > = 4.0
    • Span&lt;T&gt; 也可在 .NET Core 2.1+ / .NET Standard 2.1+ 中使用——仍然超出 .NET Framework 的范围,但仍然存在 3 年之久! (请注意,它是“.NET 5”——“.NET Framework”是旧版运行时,在 4.8 版停止)
    • @canton7 Span&lt;T&gt; 可能可用,但Encoding 缺少有用的方法,这意味着:您最终不得不在跨度上使用fixed。 NetFX 没有实现 netstandard2.1,也永远不会——只有 netstandard2.0,即使那也是一个巨大的 hack。不过,我完全同意这是正确的方法。
    • @MarcGravell Encoding.GetString(ROS) 也在 .NET Core 2.1 中引入。我从未声称任何这些在 .NET Framework 中有效,仅在 .NET Core 2.1+ / .NET Standard 2.1+ 中有效
    • @canton7 是的,完全理解 - 我只是在 OP 的前提下赞扬“我不想强迫用户,我希望 .NET Framework >= 4.0”以及 .NET Core 2.1/.NET Standard 2.1 上的 cmets;我也完全相信我们会尽可能放弃 netfx:blog.marcgravell.com/2020/01/…
    【解决方案2】:

    如果可能,您应该绝对在 canton7 here 的同一问题上使用 span 方法。

    这个答案只是为了补充问题中的一些信息,具体来说:

    我不介意在这种特殊情况下使用 unsafe(但是来自 C,我不知道这样做的后果)

    此信息相关的主要时间是:

    • 跨度在您的目标平台中不可用(或无法与您正在使用的 API 一起使用)
    • 您正在通过 P/Invoke 与非托管库交谈

    只是添加一些术语:byte* 是一个 非托管 指针(“不安全”),ref byte 是一个托管 指针(“安全”,相对请讲)。跨度基本上是“托管指针,加上长度”;所以:Span&lt;byte&gt;(或ReadOnlySpan&lt;byte&gt;)与一对byte*和int(长度)大致相当(在非管理土地上)。巨大的区别在于垃圾收集器理解托管指针(包括跟踪它们所接触的内容,并在决定移动内存时修复它们),并且甚至不查看 非托管指针(它们只是不透明的本机整数) - 所以:保持一切托管可以避免很多问题。

    因此,如果您获得 unmanaged 指向数据的指针,那么您的工作就是确保数据在您移动时不会被移动使用它,这会使您的指针无效。为此,您通常使用fixed 关键字,即(在unsafe 块中):

    uint[] data = ...
    fixed (uint* u32ptr = data)
    {
        // we can coerce pointers:
        byte* bptr = (byte*)u32ptr;
    
        // TODO: and now we can use that byte* (along with data.Length * sizeof(uint))
        // to pass to e.g. Encoding methods
        return Encoding.UTF32.GetString(bptr, data.Length * sizeof(uint));
    }
    

    重要的是不要以可以转义 fixed 块的方式存储这样的指针(此处为u32ptr 或bptr)。例如,将其存储在字段中。只要你不这样做那个,你就没事:垃圾收集器知道如何解释fixed,并且知道当线程在里面时它不能移动data地区; fixed 是一种非常低成本的临时固定数据方式。有一种 second 方式,用于更长的运行需求 - 通常是因为您将 managed 内存交给了 unmanaged API,然后该 API 会保存指针作为一个字段比在fixed 区域内使用的P/Invoke 调用更长 - GCHandle 类型为GCHandleType.Pinned - 但通常应该避免这种情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-02
      • 1970-01-01
      • 2011-03-05
      • 2011-02-12
      • 2020-12-14
      • 1970-01-01
      相关资源
      最近更新 更多