byte[] 是 .NET 中處理二進位資料的核心型別。透過擴充方法封裝常用操作,能大幅提升程式碼可讀性與復用性。本文提供 8 大類實用擴充方法完整實作。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public static class ByteArrayExtensions
{
// 所有擴充方法定義於此
}
// 單一位元組
public static string ToHex(this byte b, bool upperCase = true)
=> b.ToString(upperCase ? "X2" : "x2");
// 位元組陣列 → 連續 Hex
public static string ToHex(this IEnumerable bytes, bool upperCase = true, string separator = "")
{
if (bytes == null) return string.Empty;
var format = upperCase ? "X2" : "x2";
var sb = new StringBuilder();
bool first = true;
foreach (byte b in bytes)
{
if (!first && !string.IsNullOrEmpty(separator))
sb.Append(separator);
sb.Append(b.ToString(format));
first = false;
}
return sb.ToString();
}
// Hex → 位元組陣列
public static byte[] FromHex(this string hex)
{
if (string.IsNullOrEmpty(hex)) return Array.Empty();
hex = hex.Replace(" ", "").Replace("-", "").Replace(":", "");
if (hex.Length % 2 != 0) throw new ArgumentException("Hex 長度必須為偶數");
var bytes = new byte[hex.Length / 2];
for (int i = 0; i < bytes.Length; i++)
bytes[i] = byte.Parse(hex.Substring(i * 2, 2), NumberStyles.HexNumber);
return bytes;
}
public static string ToBase64(this byte[] bytes, Base64FormattingOptions options = Base64FormattingOptions.None)
=> bytes == null ? null : Convert.ToBase64String(bytes, options);
public static byte[] FromBase64(this string base64)
=> string.IsNullOrEmpty(base64) ? Array.Empty() : Convert.FromBase64String(base64);
// URL 安全 Base64 (無 + / =)
public static string ToBase64UrlSafe(this byte[] bytes)
=> bytes == null ? null : Convert.ToBase64String(bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_');
public static byte[] FromBase64UrlSafe(this string base64Url)
{
if (string.IsNullOrEmpty(base64Url)) return Array.Empty();
string base64 = base64Url.Replace('-', '+').Replace('_', '/');
switch (base64.Length % 4) { case 2: base64 += "=="; break; case 3: base64 += "="; break; }
return Convert.FromBase64String(base64);
}
public static int ToInt32(this byte[] bytes, int startIndex = 0, bool littleEndian = true)
{
if (bytes == null || startIndex < 0 || startIndex + 4 > bytes.Length)
throw new ArgumentException("Invalid range");
return littleEndian
? BitConverter.ToInt32(bytes, startIndex)
: BitConverter.ToInt32(bytes.Reverse().Skip(startIndex).Take(4).ToArray(), 0);
}
public static long ToInt64(this byte[] bytes, int startIndex = 0, bool littleEndian = true)
{
if (bytes == null || startIndex < 0 || startIndex + 8 > bytes.Length)
throw new ArgumentException("Invalid range");
return littleEndian
? BitConverter.ToInt64(bytes, startIndex)
: BitConverter.ToInt64(bytes.Reverse().Skip(startIndex).Take(8).ToArray(), 0);
}
public static short ToInt16(this byte[] bytes, int startIndex = 0, bool littleEndian = true)
=> littleEndian ? BitConverter.ToInt16(bytes, startIndex)
: BitConverter.ToInt16(bytes.Reverse().Skip(startIndex).Take(2).ToArray(), 0);
public static float ToSingle(this byte[] bytes, int startIndex = 0, bool littleEndian = true)
=> littleEndian ? BitConverter.ToSingle(bytes, startIndex)
: BitConverter.ToSingle(bytes.Reverse().Skip(startIndex).Take(4).ToArray(), 0);
public static double ToDouble(this byte[] bytes, int startIndex = 0, bool littleEndian = true)
=> littleEndian ? BitConverter.ToDouble(bytes, startIndex)
: BitConverter.ToDouble(bytes.Reverse().Skip(startIndex).Take(8).ToArray(), 0);
// 反向:型別 → 位元組陣列
public static byte[] GetBytes(this int value, bool littleEndian = true)
=> littleEndian ? BitConverter.GetBytes(value) : BitConverter.GetBytes(value).Reverse().ToArray();
public static byte[] GetBytes(this long value, bool littleEndian = true)
=> littleEndian ? BitConverter.GetBytes(value) : BitConverter.GetBytes(value).Reverse().ToArray();
// 字串編碼/解碼
public static string Decode(this byte[] bytes, Encoding encoding)
=> bytes == null ? null : (encoding ?? Encoding.UTF8).GetString(bytes);
public static byte[] Encode(this string text, Encoding encoding)
=> (encoding ?? Encoding.UTF8).GetBytes(text ?? string.Empty);
public static byte[] Hash(this byte[] data, HashAlgorithmName algorithmName)
{
if (data == null) throw new ArgumentNullException(nameof(data));
using var algo = HashAlgorithm.Create(algorithmName.Name);
return algo?.ComputeHash(data) ?? throw new ArgumentException("Unsupported algorithm");
}
public static byte[] Hash(this byte[] data, string algorithmName = "SHA256")
=> data.Hash(HashAlgorithmName.FromString(algorithmName));
// 常用演算法快捷方法
public static byte[] MD5(this byte[] data) => data.Hash(HashAlgorithmName.MD5);
public static byte[] SHA1(this byte[] data) => data.Hash(HashAlgorithmName.SHA1);
public static byte[] SHA256(this byte[] data) => data.Hash(HashAlgorithmName.SHA256);
public static byte[] SHA384(this byte[] data) => data.Hash(HashAlgorithmName.SHA384);
public static byte[] SHA512(this byte[] data) => data.Hash(HashAlgorithmName.SHA512);
// HMAC
public static byte[] Hmac(this byte[] data, byte[] key, HashAlgorithmName algorithmName)
{
using var hmac = new HMACSHA256(key); // 或依 algorithmName 建立對應 HMAC
return hmac.ComputeHash(data);
}
// 索引 0 = 最低有效位 (LSB)
public static bool GetBit(this byte b, int index)
{
if (index < 0 || index > 7) throw new ArgumentOutOfRangeException(nameof(index));
return (b & (1 << index)) != 0;
}
public static byte SetBit(this byte b, int index)
{
if (index < 0 || index > 7) throw new ArgumentOutOfRangeException(nameof(index));
return (byte)(b | (1 << index));
}
public static byte ClearBit(this byte b, int index)
{
if (index < 0 || index > 7) throw new ArgumentOutOfRangeException(nameof(index));
return (byte)(b & ~(1 << index));
}
public static byte ToggleBit(this byte b, int index)
{
if (index < 0 || index > 7) throw new ArgumentOutOfRangeException(nameof(index));
return (byte)(b ^ (1 << index));
}
// 陣列層級位元操作
public static bool GetBit(this byte[] bytes, long bitIndex)
{
if (bytes == null || bitIndex < 0 || bitIndex >= bytes.Length * 8L)
throw new ArgumentOutOfRangeException(nameof(bitIndex));
int byteIndex = (int)(bitIndex / 8);
int bitPos = (int)(bitIndex % 8);
return bytes[byteIndex].GetBit(bitPos);
}
public static void SaveToFile(this byte[] data, string path, FileMode mode = FileMode.Create)
{
if (data == null) throw new ArgumentNullException(nameof(data));
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path)));
File.WriteAllBytes(path, data); // 內部已處理開啟/關閉
}
// 非同步版本 (.NET 4.5+)
public static async Task SaveToFileAsync(this byte[] data, string path, CancellationToken ct = default)
{
if (data == null) throw new ArgumentNullException(nameof(data));
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path)));
using var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true);
await fs.WriteAsync(data, 0, data.Length, ct);
}
public static MemoryStream ToMemoryStream(this byte[] data, bool writable = false, bool publiclyVisible = true)
=> data == null ? new MemoryStream() : new MemoryStream(data, 0, data.Length, writable, publiclyVisible);
public static MemoryStream ToMemoryStream(this byte[] data, int index, int count)
=> data == null ? new MemoryStream() : new MemoryStream(data, index, count);
// 避免複製:建立包裝串流 (唯讀)
public static UnmanagedMemoryStream AsUnmanagedStream(this byte[] data)
{
if (data == null || data.Length == 0) return new UnmanagedMemoryStream();
// 需 unsafe context 和 fixed,或使用 MemoryMarshal (.NET 5+)
return new UnmanagedMemoryStream(data, data.Length);
}
// GZip 壓縮
public static byte[] GZipCompress(this byte[] data)
{
if (data == null || data.Length == 0) return Array.Empty();
using var ms = new MemoryStream();
using (var gzip = new GZipStream(ms, CompressionMode.Compress, true))
gzip.Write(data, 0, data.Length);
return ms.ToArray();
}
public static byte[] GZipDecompress(this byte[] data)
{
if (data == null || data.Length == 0) return Array.Empty();
using var ms = new MemoryStream(data);
using var gzip = new GZipStream(ms, CompressionMode.Decompress);
using var outMs = new MemoryStream();
gzip.CopyTo(outMs);
return outMs.ToArray();
}
// AES 加密 (需金鑰管理)
public static byte[] AesEncrypt(this byte[] data, byte[] key, byte[] iv)
{
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var encryptor = aes.CreateEncryptor();
return encryptor.TransformFinalBlock(data, 0, data.Length);
}
public static byte[] AesDecrypt(this byte[] data, byte[] key, byte[] iv)
{
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var decryptor = aes.CreateDecryptor();
return decryptor.TransformFinalBlock(data, 0, data.Length);
}
// 分割大陣列
public static IEnumerable Chunk(this byte[] data, int chunkSize)
{
if (data == null) yield break;
for (int i = 0; i < data.Length; i += chunkSize)
yield return data.Skip(i).Take(chunkSize).ToArray();
}
var data = "Hello World".Encode(Encoding.UTF8);
// Hex 顯示
Console.WriteLine($"Hex: {data.ToHex()}"); // 48 65 6C 6C 6F 20 57 6F 72 6C 64
// Base64
Console.WriteLine($"Base64: {data.ToBase64()}"); // SGVsbG8gV29ybGQ=
// SHA256
Console.WriteLine($"SHA256: {data.SHA256().ToHex()}");
// 儲存檔案
data.SaveToFile("output.bin");
// 位元操作
byte flags = 0;
flags = flags.SetBit(0).SetBit(3); // 設定第 0、3 位
Console.WriteLine($"Flags: {flags:X2}"); // 09
// 記憶體串流
using var ms = data.ToMemoryStream();
using var reader = new BinaryReader(ms);
Console.WriteLine($"Read: {reader.ReadString()}");
將這些擴充方法整理為專案共用函式庫,能統一團隊處理二進位資料的風格,減少重複造輪子,提升開發效率與程式碼品質。