C#处理类型和二进制数据转换并提高程序性能

目录
  • C# 原语类型
  • 1,利用 Buffer 优化数组性能
  • 2,BinaryPrimitives 细粒度操作字节数组
    • 提高代码安全性
  • 3,BitConverter、MemoryMarshal
  • 4,Marshal
  • 实践
  • 更高性能

C# 原语类型

按照内存分配来区分,C# 有值类型、引用类型;

按照基础类型类型来分,C# 有 内置类型、通用类型、自定义类型、匿名类型、元组类型、CTS类型(通用类型系统);

C# 的基础类型包括:

  • 整型: sbyte, byte, short, ushort, int, uint, long, ulong
  • 实数类型: float, double, decimal
  • 字符类型: char
  • 布尔类型: bool
  • 字符串类型: string

C# 中的原语类型,是基础类型中的值类型,不包括 string。原语类型可以使用 sizeof() 来获取字节大小,除 bool 外,都有 MaxValueMinValue 两个字段。

sizeof(uint);
uint.MaxValue
uint.MinValue

我们也可以在泛型上进行区分,上面的教程类型,除了 string,其他类型都是 struct。

<T>() where T : struct
{
}

1,利用 Buffer 优化数组性能

Buffer 可以操作基元类型(int、byte等)的数组,利用.NET 中的 Buffer 类,通过更快地访问内存中的数据来提高应用程序的性能。
Buffer 可以直接从基元类型的数组中,直接取出指定数量的字节,或者给其某个字节设置值。

Buffer 主要在直接操作内存数据、操作非托管内存时,使用 Buffer 可以带来安全且高性能的体验。

方法 说明
BlockCopy(Array, Int32, Array, Int32, Int32) 将指定数目的字节从起始于特定偏移量的源数组复制到起始于特定偏移量的目标数组。
ByteLength(Array) 返回指定数组中的字节数。
GetByte(Array, Int32) 检索指定数组中指定位置的字节。
MemoryCopy(Void, Void, Int64, Int64) 将指定为长整型值的一些字节从内存中的一个地址复制到另一个地址。此 API 不符合 CLS。
MemoryCopy(Void, Void, UInt64, UInt64) 将指定为无符号长整型值的一些字节从内存中的一个地址复制到另一个地址。此 API 不符合 CLS。
SetByte(Array, Int32, Byte) 将指定的值分配给指定数组中特定位置处的字节。

下面来介绍一下 Buffer 的一些使用方法。

BlockCopy 可以复制数组的一部分到另一个数组,其使用方法如下:

        int[] arr1 = new int[] { 1, 2, 3, 4, 5 };
        int[] arr2 = new int[10] { 0, 0, 0, 0, 0, 6, 7, 8, 9, 10 };

        // int = 4 byte
        // index:       0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 ... ...
        // arr1:        01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00
        // arr2:        00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00 09 00 00 00 0A 00 00 00

        // Buffer.ByteLength(arr1) == 20 ,
        // Buffer.ByteLength(arr2) == 40

        Buffer.BlockCopy(arr1, 0, arr2, 0, 19);

        for (int i = 0; i < arr2.Length; i++)
        {
            Console.Write(arr2[i] + ",");
        }

.SetByte() 则可细粒度地设置数组的值,即可以直接设置数组中任意一位的值,其使用方法如下:

        //source data:
        // 0000,0001,0002,00003,0004
        // 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
        int[] a = new int[] { 0, 1, 2, 3, 4 };
        foreach (var item in a)
        {
            Console.Write(item + ",");
        }

        Console.WriteLine("\n------\n");

        // see : https://stackoverflow.com/questions/26455843/how-are-array-values-stored-in-little-endian-vs-big-endian-architecture
        // memory save that data:
        // 0000    1000    2000    3000    4000
        for (int i = 0; i < Buffer.ByteLength(a); i++)
        {
            Console.Write(Buffer.GetByte(a, i));
            if (i != 0 && (i + 1) % 4 == 0)
                Console.Write("    ");
        }

        // 16 进制
        // 0000    1000    2000    3000    4000

        Console.WriteLine("\n------\n");

        Buffer.SetByte(a, 0, 4);
        Buffer.SetByte(a, 4, 3);
        Buffer.SetByte(a, 8, 2);
        Buffer.SetByte(a, 12, 1);
        Buffer.SetByte(a, 16, 0);

        foreach (var item in a)
        {
            Console.Write(item + ",");
        }

        Console.WriteLine("\n------\n");

建议复制代码自行测试,断点调试,观察过程。

2,BinaryPrimitives 细粒度操作字节数组

System.Buffers.Binary.BinaryPrimitives 用来以精确的方式读取或者字节数组,只能对 byte 或 byte 数组使用,其使用场景非常广泛。

BinaryPrimitives 的实现原理是 BitConverter,BinaryPrimitives 对 BitConverter 做了一些封装。BinaryPrimitives 的主要使用方式是以某种形式从 byte 或 byte 数组中读取出信息。

例如,BinaryPrimitives 在 byte 数组中,一次性读取四个字节,其示例代码如下:

        // source data:  00 01 02 03 04
        // binary data:  00000000 00000001 00000010 00000011 000001000
        byte[] arr = new byte[] { 0, 1, 2, 3, 4, };

        // read one int,4 byte
        int head = BinaryPrimitives.ReadInt32BigEndian(arr);

        // 5 byte:             00000000 00000001 00000010 00000011 000001000
        // read 4 byte(int) :  00000000 00000001 00000010 00000011
        //                     = 66051

        Console.WriteLine(head);

在 BinaryPrimitives 中有大端小端之分。在 C# 中,应该都是小端在前大端在后的,具体可能会因处理器架构而不同。
你可以使用 BitConverter.IsLittleEndian 来判断在当前处理器上,C# 程序是大端还是小端在前。

以 .Read...() 开头的方法,可以以字节为定位访问 byte 数组上的数据。

以 .Write...() 开头的方法,可以向某个位置写入数据。

下面举个例子:

        // source data:  00 01 02 03 04
        // binary data:  00000000 00000001 00000010 00000011 000001000
        byte[] arr = new byte[] { 0, 1, 2, 3, 4, };

        // read one int,4 byte
        // 5 byte:             00000000 00000001 00000010 00000011 000001000
        // read 4 byte(int) :  00000000 00000001 00000010 00000011
        //                     = 66051

        int head = BinaryPrimitives.ReadInt32BigEndian(arr);
        Console.WriteLine(head);

        // BinaryPrimitives.WriteInt32LittleEndian(arr, 1);
        BinaryPrimitives.WriteInt32BigEndian(arr.AsSpan().Slice(0, 4), 0b00000000_00000000_00000000_00000001);
        // to : 00000000 00000000 00000000 00000001 |  000001000
        // read 4 byte

        head = BinaryPrimitives.ReadInt32BigEndian(arr);
        Console.WriteLine(head);

建议复制代码自行测试,断点调试,观察过程。

提高代码安全性

C#和.NET Core 有的许多面向性能的 API,C# 和 .NET 的一大优点是可以在不牺牲内存安全性的情况下编写快速出高性能的库。我们在避免使用 unsafe 代码的情况下,通过二进制处理类,我们可以编写出高性能的代码和具有安全性的代码。

在 C# 中,我们有以下类型可以高效操作字节/内存:

  • Span 和C#类型可以快速安全地访问内存。表示任意内存的连续区域。使用 span 使我们可以序列化为托管.NET数组,堆栈分配的数组或非托管内存,而无需使用指针。.NET可以防止缓冲区溢出。
  • ref struct 、 Span
  • stackalloc 用于创建基于堆栈的数组。stackalloc 是在需要较小缓冲区时避免分配的有用工具。
  • 低级方法,并在原始类型和字节之间直接转换。MemoryMarshal.GetReference() 、Unsafe.ReadUnaligned() 、Unsafe.WriteUnaligned()
  • BinaryPrimitives具有用于在.NET基本类型和字节之间进行有效转换的辅助方法。例如,读取小尾数字节并返回无符号的64位数字。所提供的方法经过了最优化,并使用了向量化。BinaryPrimitives.ReadUInt64LittleEndianBinaryPrimitive

以 .Reverse...() 开头的方法,可以置换基元类型的大小端。

        short value = 0b00000000_00000001;
        // to endianness: 0b00000001_00000000 == 256
        BinaryPrimitives.ReverseEndianness(0b00000000_00000000_00000000_00000001);

        Console.WriteLine(BinaryPrimitives.ReverseEndianness(value));

        value = 0b00000001_00000000;
        Console.WriteLine(BinaryPrimitives.ReverseEndianness(value));
        // 1

3,BitConverter、MemoryMarshal

BitConverter 可以基元类型和 byte 相互转换,例如 int 和 byte 互转,或者任意取出、写入基元类型的任意一个字节。
其示例如下:

        // 0b...1_00000100
        int value = 260;

        // byte max value:255
        // a = 0b00000100; 丢失 int ... 00000100 之前的位数。
        byte a = (byte)value;

        // a = 4
        Console.WriteLine(a);

        // LittleEndian
        // 0b 00000100 00000001 00000000 00000000
        byte[] b = BitConverter.GetBytes(260);
        Console.WriteLine(Buffer.GetByte(b, 1)); // 4

        if (BitConverter.IsLittleEndian)
            Console.WriteLine(BinaryPrimitives.ReadInt32LittleEndian(b));
        else
            Console.WriteLine(BinaryPrimitives.ReadInt32BigEndian(b));

MemoryMarshal 提供与 Memory<T>ReadOnlyMemory<T>Span<T> 和 ReadOnlySpan<T> 进行交互操作的方法。

MemoryMarshal 在 System.Runtime.InteropServices 命名空间中。

我们先介绍 MemoryMarshal.Cast(),它可以将一种基元类型的范围强制转换为另一种基元类型的范围。

        // 1 int  = 4 byte
        // int [] {1,2}
        // 0001     0002
        var byteArray = new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 };
        Span<byte> byteSpan = byteArray.AsSpan();
        // byte to int
        Span<int> intSpan = MemoryMarshal.Cast<byte, int>(byteSpan);
        foreach (var item in intSpan)
        {
            Console.Write(item + ",");
        }

最简单的说法是,MemoryMarshal 可以将一种结构转换为另一种结构。

我们可以将一个结构转换为字节:

public struct Test
{
    public int A;
    public int B;
    public int C;
}

... ...

        Test test = new Test()
        {
            A = 1,
            B = 2,
            C = 3
        };
        var testArray = new Test[] { test };
        ReadOnlySpan<byte> tmp = MemoryMarshal.AsBytes(testArray.AsSpan());

        // socket.Send(tmp); ...

还可以逆向还原字节为结构体:

        // bytes = socket.Accept(); ..
        ReadOnlySpan<Test> testSpan = MemoryMarshal.Cast<byte,Test>(tmp);

        // or
        Test testSpan = MemoryMarshal.Read<Test>(tmp);

例如,我们要对比两个结构体数组中,每个结构体是否相等,可以采用以下代码:

        static void Main(string[] args)
        {
            int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            int[] b = new int[] { 1, 2, 3, 4, 5, 6, 7, 0, 9 };
            _ = Compare64(a,b);
        }

        private static bool Compare64<T>(T[] t1, T[] t2)
            where T : struct
        {
            var l1 = MemoryMarshal.Cast<T, long>(t1);
            var l2 = MemoryMarshal.Cast<T, long>(t2);

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

后面有个更好的性能提升方案。

程序员基本都学习过 C 语言,应该了解 C 语言中的结构体字节对齐,在 C# 中也是一样,两种类型相互转换,除了 C# 结构体转 C# 结构体,也可以 C 语言结构体转 C# 结构体,但是要考虑好字节对齐,如果两个结构体所占用的内存大小不一样,则可能在转换时出现数据丢失或出现错误。

4,Marshal

Marshal 提供了用于分配非托管内存,复制非托管内存块以及将托管类型转换为非托管类型的方法的集合,以及与非托管代码进行交互时使用的其他方法,或者用来确定对象的大小。

例如,来确定 C# 中的一些类型大小:

            Console.WriteLine("SystemDefaultCharSize={0}, SystemMaxDBCSCharSize={1}",
         Marshal.SystemDefaultCharSize, Marshal.SystemMaxDBCSCharSize);

输出 char 占用的字节数。

例如,在调用非托管代码时,需要传递函数指针,C# 一般使用委托传递,很多时候为了避免各种内存问题异常问题,需要转换为指针传递。

IntPtr p = Marshal.GetFunctionPointerForDelegate(_overrideCompileMethod)

Marshal 也可以很方便地获得一个结构体的字节大小:

public struct Point
{
    public Int32 x, y;
}

Marshal.SizeOf(typeof(Point));

从非托管内存中分配一块内存和释放内存,我们可以避免 usafe 代码的使用,代码示例:

        IntPtr hglobal = Marshal.AllocHGlobal(100);
        Marshal.FreeHGlobal(hglobal);

实践

合理利用前面提到的二进制处理类,可以在很多方面提升代码性能,在前面的学习中,我们大概了解这些对象,但是有什么应用场景?真的能够提升性能?有没有练习代码?

这里笔者举个例子,如何比较两个 byte[] 数组是否相等?
最简单的代码示例如下:

        public bool ForBytes(byte[] a,byte[] b)
        {
            if (a.Length != b.Length)
                return false;

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

这个代码很简单,循环遍历字节数组,一个个判断是否相等。

如果用上前面的二进制处理对象类,则可以这样写代码:

        private static bool EqualsBytes(byte[] b1, byte[] b2)
        {
            var a = b1.AsSpan();
            var b = b2.AsSpan();
            Span<byte> copy1 = default;
            Span<byte> copy2 = default;

            if (a.Length != b.Length)
                return false;

            for (int i = 0; i < a.Length;)
            {
                if (a.Length - 8 > i)
                {
                    copy1 = a.Slice(i, 8);
                    copy2 = b.Slice(i, 8);
                    if (BinaryPrimitives.ReadUInt64BigEndian(copy1) != BinaryPrimitives.ReadUInt64BigEndian(copy2))
                        return false;
                    i += 8;
                    continue;
                }

                if (a[i] != b[i])
                    return false;
                i++;
            }
            return true;
        }

你可能会在想,第二种方法,这么多代码,这么多判断,还有各种函数调用,还多创建了一些对象,这特么能够提升速度?这样会不会消耗更多内存??? 别急,你可以使用以下完整代码测试:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using System;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
using System.Text;

namespace BenTest
{
    [SimpleJob(RuntimeMoniker.NetCoreApp31)]
    [SimpleJob(RuntimeMoniker.CoreRt31)]
    [RPlotExporter]
    public class Test
    {
        private byte[] _a = Encoding.UTF8.GetBytes("5456456456444444444444156456454564444444444444444444444444444444444444444777777777777777777777711111111111116666666666666");
        private byte[] _b = Encoding.UTF8.GetBytes("5456456456444444444444156456454564444444444444444444444444444444444444444777777777777777777777711111111111116666666666666");

        private int[] A1 = new int[] { 41544444, 4487, 841, 8787, 4415, 7, 458, 4897, 87897, 815, 485, 4848, 787, 41, 5489, 74878, 84, 89787, 8456, 4857489, 784, 85489, 47 };
        private int[] B2 = new int[] { 41544444, 4487, 841, 8787, 4415, 7, 458, 4897, 87897, 815, 485, 4848, 787, 41, 5489, 74878, 84, 89787, 8456, 4857489, 784, 85489, 47 };

        [Benchmark]
        public bool ForBytes()
        {
            for (int i = 0; i < _a.Length; i++)
            {
                if (_a[i] != _b[i]) return false;
            }
            return true;
        }

        [Benchmark]
        public bool ForArray()
        {
            return ForArray(A1, B2);
        }

        private bool ForArray<T>(T[] b1, T[] b2) where T : struct
        {
            for (int i = 0; i < b1.Length; i++)
            {
                if (!b1[i].Equals(b2[i])) return false;
            }
            return true;
        }

        [Benchmark]
        public bool EqualsArray()
        {
            return EqualArray(A1, B2);
        }

        [Benchmark]
        public bool EqualsBytes()
        {
            var a = _a.AsSpan();
            var b = _b.AsSpan();
            Span<byte> copy1 = default;
            Span<byte> copy2 = default;

            if (a.Length != b.Length)
                return false;

            for (int i = 0; i < a.Length;)
            {
                if (a.Length - 8 > i)
                {
                    copy1 = a.Slice(i, 8);
                    copy2 = b.Slice(i, 8);
                    if (BinaryPrimitives.ReadUInt64BigEndian(copy1) != BinaryPrimitives.ReadUInt64BigEndian(copy2))
                        return false;
                    i += 8;
                    continue;
                }

                if (a[i] != b[i])
                    return false;
                i++;
            }
            return true;
        }

        private bool EqualArray<T>(T[] t1, T[] t2) where T : struct
        {
            Span<byte> b1 = MemoryMarshal.AsBytes<T>(t1.AsSpan());
            Span<byte> b2 = MemoryMarshal.AsBytes<T>(t2.AsSpan());

            Span<byte> copy1 = default;
            Span<byte> copy2 = default;

            if (b1.Length != b2.Length)
                return false;

            for (int i = 0; i < b1.Length;)
            {
                if (b1.Length - 8 > i)
                {
                    copy1 = b1.Slice(i, 8);
                    copy2 = b2.Slice(i, 8);
                    if (BinaryPrimitives.ReadUInt64BigEndian(copy1) != BinaryPrimitives.ReadUInt64BigEndian(copy2))
                        return false;
                    i += 8;
                    continue;
                }

                if (b1[i] != b2[i])
                    return false;
                i++;
            }
            return true;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<Test>();
            Console.ReadKey();
        }
    }
}

使用 BenchmarkDotNet 的测试结果如下:

BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1052 (21H1/May2021Update)
Intel Core i7-10700 CPU 2.90GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=5.0.301
  [Host]        : .NET Core 3.1.16 (CoreCLR 4.700.21.26205, CoreFX 4.700.21.26205), X64 RyuJIT
  .NET Core 3.1 : .NET Core 3.1.16 (CoreCLR 4.700.21.26205, CoreFX 4.700.21.26205), X64 RyuJIT

|      Method |           Job |       Runtime |     Mean |    Error |   StdDev |
|------------ |-------------- |-------------- |---------:|---------:|---------:|
|    ForBytes | .NET Core 3.1 | .NET Core 3.1 | 76.95 ns | 0.064 ns | 0.053 ns |
|    ForArray | .NET Core 3.1 | .NET Core 3.1 | 66.37 ns | 1.258 ns | 1.177 ns |
| EqualsArray | .NET Core 3.1 | .NET Core 3.1 | 17.91 ns | 0.027 ns | 0.024 ns |
| EqualsBytes | .NET Core 3.1 | .NET Core 3.1 | 26.26 ns | 0.432 ns | 0.383 ns |

可以看到,byte[] 比较中,使用了二进制对象的方式,耗时下降了近 60ns,而在 struct 的比较中,耗时也下降了 40ns。

在第二种代码中,我们使用了 Span、切片、 MemoryMarshal、BinaryPrimitives,这些用法都可以给我们的程序性能带来很大的提升。

这里示例虽然使用了 Span 等,其最主要是利用了 64位 CPU ,64位 CPU 能够一次性读取 8个字节(64位),因此我们使用 ReadUInt64BigEndian 一次读取从字节数组中读取 8 个字节去进行比较。如果字节数组长度为 1024 ,那么第二种方法只需要 比较 128次。

当然,这里并不是这种代码性能是最强的,因为 CLR 有很多底层方法具有更猛的性能。不过,我们也看到了,合理使用这些类型,能够很大程度上提高代码性能。上面的数组对比只是一个简单的例子,在实际项目中,我们也可以挖掘更多使用场景。

更高性能

虽然第二种方法,快了几倍,但是性能还不够强劲,我们可以利用 Span 中的 API,来实现更快的比较。

        [Benchmark]
        public bool SpanEqual()
        {
            return SpanEqual(_a,_b);
        }
        private bool SpanEqual(byte[] a, byte[] b)
        {
            return a.AsSpan().SequenceEqual(b);
        }

可以试试

StructuralComparisons.StructuralEqualityComparer.Equals(a, b);

性能测试结果:

|      Method |           Job |       Runtime |      Mean |     Error |    StdDev |
|------------ |-------------- |-------------- |----------:|----------:|----------:|
|    ForBytes | .NET Core 3.1 | .NET Core 3.1 | 77.025 ns | 0.0502 ns | 0.0419 ns |
|    ForArray | .NET Core 3.1 | .NET Core 3.1 | 66.192 ns | 0.6127 ns | 0.5117 ns |
| EqualsArray | .NET Core 3.1 | .NET Core 3.1 | 17.897 ns | 0.0122 ns | 0.0108 ns |
| EqualsBytes | .NET Core 3.1 | .NET Core 3.1 | 25.722 ns | 0.4584 ns | 0.4287 ns |
|   SpanEqual | .NET Core 3.1 | .NET Core 3.1 |  4.736 ns | 0.0099 ns | 0.0093 ns |

可以看到,Span.SequenceEqual() 的速度简直是碾压。
对于 C# 中的二进制处理技巧就介绍到这里,阅读 CLR 源码 时,我们可以学习到很多骚操作,读者可以多阅读 CLR 源码,对技术提升有很大的帮助。

到此这篇关于C#处理类型和二进制数据转换并提高程序性能的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • c#二进制逆序方法详解

    原题 一个整数,可以表示为二进制的形式,请给出尽可能多的方法对二进制进行逆序操作. 例如:10000110 11011000的逆序为 00011011 01100001 分析 题目中说是一个整数,对它的二进制进行逆序.并不是一个01字符串,或者01的数组.那么我们该如何解决这个问题呢?方法还是比较多的,有的中规中矩.有的非常巧妙.我们要掌握中规中规的方法,见识更多的巧妙的方法.慢慢的,能够举一反三,在遇到新的问题时,能够有灵思妙想. 最直接的方法 直接的方法,很容易想到:有如下代码: 复制代码

  • C#实现文件与二进制互转并存入数据库

    //这个方法是浏览文件对象 private void button1_Click(object sender, EventArgs e) { //用户打开文件浏览 using (OpenFileDialog dialog = new OpenFileDialog()) { //只能单选一个文件 dialog.Multiselect = false; //选择一个文件 if (dialog.ShowDialog() == DialogResult.OK) { try { //把选择的文件路径给tx

  • C#对二进制数据进行base64编码的方法

    本文实例讲述了C#对二进制数据进行base64编码的方法.分享给大家供大家参考.具体实现方法如下: using System; using System.IO; static class MyModClass { public static string Base64EncodeBytes(this byte[] inBytes) { return (Convert.ToBase64String(inBytes)); } } 希望本文所述对大家的C#程序设计有所帮助.

  • C#实现把图片转换成二进制以及把二进制转换成图片的方法示例

    本文实例讲述了C#实现把图片转换成二进制以及把二进制转换成图片的方法.分享给大家供大家参考,具体如下: private void button1_Click(object sender, EventArgs e) { string path = this.textBox1.Text; byte[] imgBytesIn = SaveImage(path); ShowImgByByte(imgBytesIn); //Parameters.Add("@Photo", SqlDbType.B

  • 关于C#转换二进制所引起的一些思考

    前言 最近遇到很有意思转换二进制的问题,有部分童鞋俨然已了解,可能也有一部分童鞋没碰到过也就不知情,这里我们来深入学习下转换二进制所带来的问题. 二进制转换问题 假设现在我们有一个int类型的数据,它的范围区间暂且定在0-15之间,我们需要将其转换为二进制,然后获取二进制中的每一位,若不足4位则0填充.看似很简单是不是,直接通过C#内置APi即可达到此需求,如下: var binary = Convert.ToString(7, 2).PadLeft(4, '0').ToArray(); 上述将

  • C#二进制序列化实例分析

    本文实例讲述了C#二进制序列化的方法.分享给大家供大家参考.具体如下: using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization; namespace WebApplication1.Serialize { public partial class Binary1 : System.Web.UI.Page { protected void Page_Load(object se

  • C#实现的基于二进制读写文件操作示例

    本文实例讲述了C#实现的基于二进制读写文件操作.分享给大家供大家参考,具体如下: using System; using System.IO; class MyStream { private const string FILE_NAME = "Test.data"; public static void Main(String[] args) { // Create the new, empty data file. if (File.Exists(FILE_NAME)) { Con

  • C# 进制转换的实现(二进制、十六进制、十进制互转)

    由于二进制数在C#中无法直接表示,所以所有二进制数都用一个字符串来表示 例如: 二进制: 1010 表示为 字符串:"1010"  int d = 10; //十进制转二进制字符串 Console.WriteLine(Convert.ToString(d,2)); //输出: 1010 //十进制转十六进制字符串 Console.WriteLine(Convert.ToString(d,16)); //输出: a //二进制字符串转十进制数 string bin = "101

  • C#处理类型和二进制数据转换并提高程序性能

    目录 C# 原语类型 1,利用 Buffer 优化数组性能 2,BinaryPrimitives 细粒度操作字节数组 提高代码安全性 3,BitConverter.MemoryMarshal 4,Marshal 实践 更高性能 C# 原语类型 按照内存分配来区分,C# 有值类型.引用类型: 按照基础类型类型来分,C# 有 内置类型.通用类型.自定义类型.匿名类型.元组类型.CTS类型(通用类型系统): C# 的基础类型包括: 整型: sbyte, byte, short, ushort, int

  • .Net Core中使用ref和Span<T>提高程序性能的实现代码

    一.前言 其实说到ref,很多同学对它已经有所了解,ref是C# 7.0的一个语言特性,它为开发人员提供了返回本地变量引用和值引用的机制. Span也是建立在ref语法基础上的一个复杂的数据类型,在文章的后半部分,我会有一个例子说明如何使用它. 二.ref关键字 不论是ref还是out关键,都是一种比较难以理解和操作的语言特性,如C语言中操作指针一样,这样的高级语法总是什么带来一些副作用,但是我不认为这有什么,而且不是每一个C#开发者都要对这些内部运行的机制有着深刻的理解,我觉得不论什么复杂的东

  • 利用nodejs读取图片并将二进制数据转换成base64格式

    目录 读取图片并将二进制数据转换成base64格式 nodejs读取服务器图片,转为base64显示在网页上 读取图片并将二进制数据转换成base64格式 首先,使用nodejs进行数据读取,需要用到nodejs的fs模块进行数据读取: fs.readFile('你的资源路径','binary',function(err,data){     if(err){         console.log(err)     }else{         console.log('数据读取成功');  

  • 在DB2中提高INSERT性能的技巧(1)

    正在看的db2教程是:在DB2中提高INSERT性能的技巧(1). INSERT 处理过程概述 首先让我们快速地看看插入一行时的处理步骤.这些步骤中的每一步都有优化的潜力,对此我们在后面会一一讨论. 在客户机准备 语句.对于动态 SQL,在语句执行前就要做这一步,此处的性能是很重要的:对于静态 SQL,这一步的性能实际上关系不大,因为语句的准备是事先完成的. 在客户机,将要插入的行的各个 列值组装起来,发送到 DB2 服务器. DB2 服务器确定将这一行插入到哪一页中. DB2 在 用于该页的缓

  • 充分利用ASP.NET的三种缓存提高站点性能的注意方法

    ASP.NET提供三种主要形式的缓存:页面级输出缓存.用户控件级输出缓存(或称为片段缓存)和缓存API. 尽早缓存:经常缓存  您应该在应用程序的每一层都实现缓存.向数据层.业务逻辑层.UI或输出层添加缓存支持.内存现在非常便宜-因此,通过以智能的方式在整个应用程序中实现缓存,可以获得很大的性能提高. 页面级输出缓存 最简单的缓存形式,只是在内存中保留为响应请求而发送的HTML的副本. 要实现页面输出缓存,只要将一条OutputCache指令添加到页面即可. <%@ OutputCache Du

  • SQL Server 聚焦存储过程性能优化、数据压缩和页压缩提高IO性能方法(一)

    前言 关于SQL Server基础系列尚未结束,还剩下最后一点内容未写,后面会继续.有园友询问我什么时候开始写SQL Server性能系列,估计还得等一段时间,最近工作也比较忙,但是会陆陆续续的更新SQL Server性能系列,本篇作为性能系列的基本引导,让大家尝尝鲜.在涉及到SQL Server性能优化时,我看到的有些文章就是一上来列出SQL Server的性能优化条例,根本没有弄清楚为什么这么做,当然也有可能是自己弄懂了,只是作为备忘录,但是到了我这里,我会遵循不仅仅是备忘录,还要让各位园友

  • 使用Function.apply()的参数数组化来提高 JavaScript程序性能的技巧

    我们再来聊聊Function.apply() 在提升程序性能方面的技巧. 我们先从 Math.max() 函数说起, Math.max后面可以接任意个参数,最后返回所有参数中的最大值. 比如 alert(Math.max(5,8)) //8 alert(Math.max(5,7,9,3,1,6)) //9 但是在很多情况下,我们需要找出数组中最大的元素. var arr=[5,7,9,1] alert(Math.max(arr)) // 这样却是不行的.一定要这样写 function getMa

  • 图片该如何优化来提高网站性能

    概述 图像是web上提供的最基本的内容类型之一.他们说一张图片胜过千言万语.但是如果你不小心的话,图片大小有时高达几十兆. 因此,虽然网络图像需要清晰明快,但它们尺寸可以缩小压缩的,使用加载时间保持在可接受的水平. 在我的网站上,我注意到我的主页的页面大小 超过了1.1MB,图片占了约88%,我还注意到我提供的图像比它们需要的大(在分辨率方面),显然,还有很多改进的空间. 我开始阅读 Addy Osmani 的优秀Essential Image Optimization电子书,并开始在我的网站上

  • EF Core通过显式编译提高查询性能

    今天,我将向您展示这些EF Core中一个很酷的功能,通过使用显式编译的查询,提高查询性能. 不过在介绍具体内容之前,需要说明一点,EF Core已经对表达式的编译使用了缓存:当您的代码需要重用以前执行的查询时,EF Core将使用哈希查找并从缓存中返回已编译的查询. 不过,您可能希望直接对查询进行编译,跳过哈希的计算和缓存查找.我们可以通过在EF静态类中下面两个方法来实现: EF.CompileQuery() EF.CompileAsyncQuery() 这些方法允许您定义一个已编译的查询,然

  • 提高正则表达式性能的几点实用建议汇总

    目录 为什么正则表达式效率很重要? 低效正则表达式的剖析 真实例子 编写高效正则表达式的指南 考虑故障场景 注意通配符标记的多次重复 不要以通配符重复开始正则表达式 只匹配你真正需要的 试着快速失败 Profile-尤其是故障案例 尽量减少从主机中提取的数据 除非绝对必要,否则不要使用组 考虑正则表达式 TPL触发器的正则表达式优化 索引 试着给点提示 尝试做出独特的提示 总结 当正则表达式通常与大型数据集相匹配时它们的编写必须高效. 为什么正则表达式效率很重要? 虽然写得好的正则表达式可能非常

随机推荐