ObjectToByteArrayExtensions Class
The ObjectToByteArrayExtensions
class provides comprehensive extensions for converting objects to byte arrays using various encodings, serialization formats, and specialized operations. This class includes support for JSON serialization, collection handling, memory management, and custom serialization patterns.
IByteSerializable Interface
Interface Definition
public interface IByteSerializable
{
byte[] ToBytes();
void FromBytes(byte[] data);
}
Example Implementation
public class CustomData : IByteSerializable
{
public int Id { get; set; }
public string Name { get; set; } = "";
public byte[] ToBytes()
{
using var builder = new ByteArrayBuilder();
return builder
.Append(Id)
.AppendUtf8String(Name)
.ToByteArray();
}
public void FromBytes(byte[] data)
{
var position = 0;
Id = data.ToInt32(ref position);
Name = data.ToUtf8String(ref position, -1);
}
}
// Usage
var custom = new CustomData { Id = 42, Name = "Test" };
var bytes = custom.ToByteArray();
var restored = new CustomData();
restored.FromBytes(bytes);
String Conversion Methods
AsciiStringToByteArray
Converts an ASCII string to a byte array.
Syntax
public static byte[] AsciiStringToByteArray(this string value)
Example
var text = "Hello World";
var asciiBytes = text.AsciiStringToByteArray();
Console.WriteLine($"ASCII bytes: {string.Join(" ", asciiBytes.Select(b => $"0x{b:X2}"))}");
// Output: 0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64
// Handle null/empty strings
string? nullString = null;
var emptyBytes = nullString.AsciiStringToByteArray(); // Returns empty array
var emptyString = "";
var emptyBytes2 = emptyString.AsciiStringToByteArray(); // Returns empty array
Utf8StringToByteArray
Converts a UTF-8 string to a byte array.
Syntax
public static byte[] Utf8StringToByteArray(this string value)
Example
var text = "Hello 世界"; // Mixed ASCII and Unicode
var utf8Bytes = text.Utf8StringToByteArray();
Console.WriteLine($"UTF-8 bytes: {string.Join(" ", utf8Bytes.Select(b => $"0x{b:X2}"))}");
// ASCII characters are same, Unicode characters use multiple bytes
// Compare with ASCII (will throw for non-ASCII characters)
var asciiSafe = "Hello World";
var asciiBytes = asciiSafe.AsciiStringToByteArray();
var utf8Bytes2 = asciiSafe.Utf8StringToByteArray();
Console.WriteLine($"ASCII and UTF-8 identical: {asciiBytes.SequenceEqual(utf8Bytes2)}"); // True
HexStringToByteArray
Converts a hexadecimal string to a byte array with flexible input format support.
Syntax
public static byte[] HexStringToByteArray(this string hexString)
Example
// Various hex string formats
var formats = new[]
{
"DEADBEEF",
"DE AD BE EF",
"DE-AD-BE-EF",
"DE:AD:BE:EF",
"0xDE 0xAD 0xBE 0xEF"
};
foreach (var format in formats)
{
var bytes = format.HexStringToByteArray();
Console.WriteLine($"'{format}' -> {string.Join(" ", bytes.Select(b => $"0x{b:X2}"))}");
// All produce: 0xDE 0xAD 0xBE 0xEF
}
// Error handling
try
{
var invalid = "INVALID".HexStringToByteArray(); // Odd number of characters
}
catch (ArgumentException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
Base64StringToByteArray
Converts a Base64 string to a byte array.
Syntax
public static byte[] Base64StringToByteArray(this string base64String)
Example
var originalData = "Hello World"u8.ToArray();
var base64 = Convert.ToBase64String(originalData);
Console.WriteLine($"Base64: {base64}");
var decodedBytes = base64.Base64StringToByteArray();
var decodedText = Encoding.UTF8.GetString(decodedBytes);
Console.WriteLine($"Decoded: {decodedText}"); // "Hello World"
// Round-trip verification
Console.WriteLine($"Round-trip successful: {originalData.SequenceEqual(decodedBytes)}");
Generic Type Conversion
ToByteArray
Converts any supported type to a byte array using the ByteArrayBuilder.
Syntax
public static byte[] ToByteArray<T>(this T value)
Example
// Primitive types
var intBytes = 42.ToByteArray();
var floatBytes = 3.14f.ToByteArray();
var boolBytes = true.ToByteArray();
// Complex types
var dateBytes = DateTime.Now.ToByteArray();
var guidBytes = Guid.NewGuid().ToByteArray();
// Strings
var stringBytes = "Hello World".ToByteArray(); // UTF-8 encoding
// Arrays
var arrayBytes = new int[] { 1, 2, 3, 4 }.ToByteArray();
// Null handling
string? nullValue = null;
var nullBytes = nullValue.ToByteArray(); // Returns empty array
Console.WriteLine($"Int32 bytes: {intBytes.Length}"); // 4
Console.WriteLine($"DateTime bytes: {dateBytes.Length}"); // 8
JSON Serialization
ToJsonByteArray
Serializes an object to JSON as a byte array using System.Text.Json.
Syntax
public static byte[] ToJsonByteArray<T>(this T value, JsonSerializerOptions? options = null)
Example
var person = new { Name = "John", Age = 30, Active = true };
var jsonBytes = person.ToJsonByteArray();
var jsonString = Encoding.UTF8.GetString(jsonBytes);
Console.WriteLine($"JSON: {jsonString}");
// Output: {"Name":"John","Age":30,"Active":true}
// Custom serialization options
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
var formattedJsonBytes = person.ToJsonByteArray(options);
var formattedJson = Encoding.UTF8.GetString(formattedJsonBytes);
Console.WriteLine(formattedJson);
FromJsonByteArray
Deserializes a JSON byte array back to an object.
Syntax
public static T? FromJsonByteArray<T>(this byte[] jsonBytes, JsonSerializerOptions? options = null)
Example
public record Person(string Name, int Age, bool Active);
var original = new Person("Alice", 25, true);
var jsonBytes = original.ToJsonByteArray();
var restored = jsonBytes.FromJsonByteArray<Person>();
Console.WriteLine($"Original: {original}");
Console.WriteLine($"Restored: {restored}");
Console.WriteLine($"Equal: {original.Equals(restored)}"); // True
// Handle deserialization errors
try
{
var invalidJson = "{ invalid json }"u8.ToArray();
var result = invalidJson.FromJsonByteArray<Person>();
}
catch (JsonException ex)
{
Console.WriteLine($"JSON error: {ex.Message}");
}
ToJsonMemory
Creates a ReadOnlyMemory
Syntax
public static ReadOnlyMemory<byte> ToJsonMemory<T>(this T value, JsonSerializerOptions? options = null)
Example
var data = new { Values = new[] { 1, 2, 3 }, Name = "Test" };
var jsonMemory = data.ToJsonMemory();
// Use with span operations
var jsonSpan = jsonMemory.Span;
Console.WriteLine($"JSON length: {jsonSpan.Length}");
// Convert to string for display
var jsonString = Encoding.UTF8.GetString(jsonSpan);
Console.WriteLine($"JSON: {jsonString}");
// Memory-efficient processing
using var stream = new MemoryStream();
stream.Write(jsonSpan);
Collection Serialization
Collection ToByteArray
Serializes collections with length-prefixed items.
Syntax
public static byte[] ToByteArray<T>(this IEnumerable<T> collection, Func<T, byte[]> itemSerializer)
Example
var numbers = new[] { 42, 123, 456 };
// Serialize with custom serializer
var serializedNumbers = numbers.ToByteArray(n => n.ToByteArray());
Console.WriteLine($"Serialized length: {serializedNumbers.Length}");
// Format: [count:4][len:4][data:4][len:4][data:4][len:4][data:4]
// Serialize strings
var strings = new[] { "Hello", "World", "Test" };
var serializedStrings = strings.ToByteArray(s => s.Utf8StringToByteArray());
// Complex objects
var people = new[]
{
new { Name = "Alice", Age = 30 },
new { Name = "Bob", Age = 25 }
};
var serializedPeople = people.ToByteArray(p => p.ToJsonByteArray());
FromByteArrayToList
Deserializes a byte array back to a list of objects.
Syntax
public static IList<T> FromByteArrayToList<T>(this byte[] data, Func<byte[], T> itemDeserializer)
Example
// Deserialize numbers
var originalNumbers = new[] { 42, 123, 456 };
var serialized = originalNumbers.ToByteArray(n => n.ToByteArray());
var restored = serialized.FromByteArrayToList(bytes => bytes.ToInt32());
Console.WriteLine($"Original: [{string.Join(", ", originalNumbers)}]");
Console.WriteLine($"Restored: [{string.Join(", ", restored)}]");
Console.WriteLine($"Equal: {originalNumbers.SequenceEqual(restored)}");
// Round-trip with strings
var originalStrings = new[] { "Hello", "World", "Test" };
var stringsSerialized = originalStrings.ToByteArray(s => s.Utf8StringToByteArray());
var stringsRestored = stringsSerialized.FromByteArrayToList(bytes => bytes.ToUtf8String());
Console.WriteLine($"Strings equal: {originalStrings.SequenceEqual(stringsRestored)}");
Dictionary Serialization
Serializes dictionaries with key-value pairs.
Syntax
public static byte[] ToByteArray<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary,
Func<TKey, byte[]> keySerializer,
Func<TValue, byte[]> valueSerializer)
Example
var settings = new Dictionary<string, object>
{
["MaxRetries"] = 5,
["Timeout"] = 30.0,
["EnableLogging"] = true,
["ServiceUrl"] = "https://api.example.com"
};
var serialized = settings.ToByteArray(
key => key.Utf8StringToByteArray(),
value => value.ToJsonByteArray()
);
Console.WriteLine($"Dictionary serialized to {serialized.Length} bytes");
// Custom strongly-typed dictionary
var scores = new Dictionary<string, int>
{
["Alice"] = 95,
["Bob"] = 87,
["Charlie"] = 92
};
var scoresSerialized = scores.ToByteArray(
name => name.Utf8StringToByteArray(),
score => score.ToByteArray()
);
Span and Memory Operations
ReadOnlySpan ToByteArray
Converts a span to a byte array.
Syntax
public static byte[] ToByteArray(this ReadOnlySpan<byte> span)
public static byte[] ToByteArray(this ReadOnlyMemory<byte> memory)
Example
var originalArray = new byte[] { 1, 2, 3, 4, 5 };
// Create spans
var span = originalArray.AsSpan(1, 3); // [2, 3, 4]
var spanArray = span.ToByteArray();
var memory = originalArray.AsMemory(2, 2); // [3, 4]
var memoryArray = memory.ToByteArray();
Console.WriteLine($"Original: [{string.Join(", ", originalArray)}]");
Console.WriteLine($"Span: [{string.Join(", ", spanArray)}]");
Console.WriteLine($"Memory: [{string.Join(", ", memoryArray)}]");
TryWriteToSpan
Writes an object to a span using a custom serializer.
Syntax
public static int TryWriteToSpan<T>(this T value, Span<byte> destination, Func<T, Span<byte>, int> serializer)
Example
// Custom serializer for a point structure
static int SerializePoint((int X, int Y) point, Span<byte> dest)
{
if (dest.Length < 8) return 0;
BitConverter.TryWriteBytes(dest[0..4], point.X);
BitConverter.TryWriteBytes(dest[4..8], point.Y);
return 8;
}
var point = (X: 100, Y: 200);
var buffer = new byte[16];
var bytesWritten = point.TryWriteToSpan(buffer, SerializePoint);
Console.WriteLine($"Bytes written: {bytesWritten}");
Console.WriteLine($"Buffer: {string.Join(" ", buffer.Take(bytesWritten).Select(b => $"0x{b:X2}"))}");
ArrayPool Integration
Efficient memory management using ArrayPool.
Syntax
public static (byte[] buffer, int length) ToRentedBuffer<T>(
this T value,
Func<T, byte[]> serializer,
int minimumLength = 1024)
public static void ReturnRentedBuffer(byte[] buffer, bool clearArray = false)
Example
var largeObject = new { Data = Enumerable.Range(0, 1000).ToArray() };
// Rent buffer for serialization
var (buffer, length) = largeObject.ToRentedBuffer(obj => obj.ToJsonByteArray(), 4096);
try
{
// Use buffer
var actualData = buffer.AsSpan(0, length);
Console.WriteLine($"Serialized {length} bytes into rented buffer of {buffer.Length}");
// Process the data
ProcessSerializedData(actualData);
}
finally
{
// Always return the buffer
ObjectToByteArrayExtensions.ReturnRentedBuffer(buffer, clearArray: true);
}
static void ProcessSerializedData(ReadOnlySpan<byte> data)
{
// Process without additional allocations
Console.WriteLine($"Processing {data.Length} bytes");
}
Custom Serialization Support
IByteSerializable ToByteArray
Serializes objects implementing IByteSerializable.
Example
public class NetworkPacket : IByteSerializable
{
public byte PacketType { get; set; }
public ushort PacketId { get; set; }
public byte[] Payload { get; set; } = Array.Empty<byte>();
public byte[] ToBytes()
{
using var builder = new ByteArrayBuilder();
return builder
.Append(PacketType)
.Append(PacketId)
.Append((ushort)Payload.Length)
.Append(Payload)
.ToByteArray();
}
public void FromBytes(byte[] data)
{
var position = 0;
PacketType = data.ToByte(ref position);
PacketId = data.ToUInt16(ref position);
var payloadLength = data.ToUInt16(ref position);
Payload = data.Skip(position).Take(payloadLength).ToArray();
}
}
// Usage
var packet = new NetworkPacket
{
PacketType = 0x01,
PacketId = 1234,
Payload = "Hello"u8.ToArray()
};
var bytes = packet.ToByteArray(); // Uses IByteSerializable implementation
Custom Serializer Function
Use custom serialization logic.
Syntax
public static byte[] ToByteArray<T>(this T value, Func<T, byte[]> customSerializer)
Example
// Custom serializer for DateTime as Unix timestamp
static byte[] SerializeDateTimeAsUnix(DateTime dt)
{
var unixTimestamp = ((DateTimeOffset)dt).ToUnixTimeSeconds();
return BitConverter.GetBytes(unixTimestamp);
}
var now = DateTime.Now;
var customBytes = now.ToByteArray(SerializeDateTimeAsUnix);
Console.WriteLine($"Custom DateTime serialization: {customBytes.Length} bytes");
// Compare with default serialization
var defaultBytes = now.ToByteArray();
Console.WriteLine($"Default DateTime serialization: {defaultBytes.Length} bytes");
Cached Serializer
Creates a caching serializer for expensive operations.
Syntax
public static Func<T, byte[]> CreateCachedSerializer<T>(Func<T, byte[]> baseSerializer, int maxCacheSize = 100)
where T : notnull
Example
// Expensive serialization function (simulated)
static byte[] ExpensiveSerializer(ComplexObject obj)
{
Thread.Sleep(10); // Simulate expensive operation
return obj.ToJsonByteArray();
}
// Create cached version
var cachedSerializer = ObjectToByteArrayExtensions.CreateCachedSerializer<ComplexObject>(
ExpensiveSerializer, maxCacheSize: 50);
var objects = Enumerable.Range(0, 100).Select(i => new ComplexObject { Id = i % 20 }).ToList();
// Measure performance
var stopwatch = Stopwatch.StartNew();
foreach (var obj in objects)
{
var bytes = cachedSerializer(obj); // Will cache results for repeated objects
}
stopwatch.Stop();
Console.WriteLine($"Cached serialization took: {stopwatch.ElapsedMilliseconds}ms");
// Significant speedup due to caching for repeated objects
Advanced Usage Patterns
Polymorphic Serialization
public abstract class Shape : IByteSerializable
{
public abstract byte ShapeType { get; }
public abstract byte[] ToBytes();
public abstract void FromBytes(byte[] data);
public static Shape FromTypeAndBytes(byte shapeType, byte[] data)
{
Shape shape = shapeType switch
{
1 => new Circle(),
2 => new Rectangle(),
_ => throw new NotSupportedException($"Shape type {shapeType} not supported")
};
shape.FromBytes(data);
return shape;
}
}
public class Circle : Shape
{
public override byte ShapeType => 1;
public double Radius { get; set; }
public override byte[] ToBytes()
{
using var builder = new ByteArrayBuilder();
return builder.Append(ShapeType).Append(Radius).ToByteArray();
}
public override void FromBytes(byte[] data)
{
var position = 1; // Skip type byte
Radius = data.ToDouble(ref position);
}
}
Versioned Serialization
public class VersionedData : IByteSerializable
{
public const byte CurrentVersion = 2;
public string Name { get; set; } = "";
public int Value { get; set; }
public DateTime? CreatedAt { get; set; } // Added in version 2
public byte[] ToBytes()
{
using var builder = new ByteArrayBuilder();
return builder
.Append(CurrentVersion)
.AppendUtf8String(Name)
.Append(Value)
.Append(CreatedAt?.ToBinary() ?? 0)
.ToByteArray();
}
public void FromBytes(byte[] data)
{
var position = 0;
var version = data.ToByte(ref position);
Name = data.ToUtf8String(ref position);
Value = data.ToInt32(ref position);
if (version >= 2)
{
var ticks = data.ToInt64(ref position);
CreatedAt = ticks != 0 ? DateTime.FromBinary(ticks) : null;
}
}
}
Performance Characteristics
Time Complexity
- String conversions: O(n) where n is string length
- JSON serialization: O(n) where n is object complexity
- Collection serialization: O(k×m) where k is item count and m is average item size
- Cached serialization: O(1) for cache hits, O(n) for cache misses
Memory Efficiency
- Span operations: Zero-copy when possible
- ArrayPool integration: Reduces garbage collection pressure
- Streaming serialization: Constant memory usage for large data
Best Practices
Choose Appropriate Serialization
// For interoperability: JSON
var jsonBytes = obj.ToJsonByteArray();
// For performance: Custom binary
var binaryBytes = obj.ToByteArray(CustomBinarySerializer);
// For .NET specific: Built-in types
var nativeBytes = obj.ToByteArray(); // Uses ByteArrayBuilder
Error Handling
try
{
var bytes = complexObject.ToJsonByteArray();
var restored = bytes.FromJsonByteArray<ComplexObject>();
}
catch (JsonException ex)
{
// Handle JSON serialization errors
Console.WriteLine($"JSON error: {ex.Message}");
}
catch (ArgumentException ex)
{
// Handle conversion errors
Console.WriteLine($"Conversion error: {ex.Message}");
}
Memory Management
// Use ArrayPool for large operations
var (buffer, length) = largeData.ToRentedBuffer(serialize, 8192);
try
{
// Use buffer
}
finally
{
ObjectToByteArrayExtensions.ReturnRentedBuffer(buffer, true);
}
// Use spans for zero-copy operations
ReadOnlySpan<byte> span = existingArray;
var newArray = span.ToByteArray(); // Only when necessary
Cross-References
- ByteArrayBuilder - Fluent byte array construction
- ByteArrayExtensions - Core conversion operations
- ByteArrayAsyncExtensions - Asynchronous serialization
- ByteArrayCompressionExtensions - Compression support
- ByteArrayUtilities - Analysis and debugging tools