ByteArrayExtensions.ComplexTypeConversion
This documentation covers complex type conversion extension methods in ByteArrayExtensions
. These methods handle more sophisticated data types including Version objects and strongly-typed enums with validation.
Overview
Complex type conversion methods provide safe, validated conversions for:
- Version objects from UTF-8 string representations
- Enum types with compile-time type safety and runtime validation
- Flags enums with bitwise validation
- Type safety ensuring invalid values are rejected
Version Conversion
ToVersion
Converts UTF-8 string data to System.Version objects with proper parsing.
Usage Examples
// Basic version parsing
byte[] versionData = "1.2.3.4"u8.ToArray();
var position = 0;
Version version = versionData.ToVersion(ref position);
Console.WriteLine($"Version: {version}"); // 1.2.3.4
Console.WriteLine($"Position: {position}"); // 7
// Fixed position parsing
byte[] mixedData = GetMixedData();
Version appVersion = mixedData.ToVersion(0, 10); // Read exactly 10 bytes
// File header version parsing
byte[] fileHeader = File.ReadAllBytes("data.bin");
position = 8; // Skip file signature
Version fileFormat = fileHeader.ToVersion(ref position, 6); // "2.1.0\0"
// Protocol version negotiation
byte[] handshakeData = ReceiveHandshake();
position = 0;
byte protocolType = handshakeData.ToByte(ref position);
Version protocolVersion = handshakeData.ToVersion(ref position, 8);
if (protocolVersion < new Version(2, 0))
{
throw new NotSupportedException($"Protocol version {protocolVersion} not supported");
}
// Assembly version reading
byte[] assemblyMetadata = GetAssemblyMetadata();
position = 0;
Version assemblyVersion = assemblyMetadata.ToVersion(ref position, 12);
Version fileVersion = assemblyMetadata.ToVersion(ref position, 12);
Console.WriteLine($"Assembly: {assemblyVersion}, File: {fileVersion}");
// Common version formats
byte[] semverData = "2.1.0-beta"u8.ToArray();
Version semver = semverData.ToVersion(0); // Parses as 2.1.0.0
byte[] buildData = "1.0.0.12345"u8.ToArray();
Version buildVersion = buildData.ToVersion(0); // 1.0.0.12345
ToVersionOrDefault
Safe version parsing with default value fallback for invalid or missing data.
Usage Examples
byte[] corruptData = { 0x01, 0x02, 0x03 }; // Invalid version string
var position = 0;
// Use default version for invalid data
Version defaultVersion = new Version(1, 0, 0);
Version parsed = corruptData.ToVersionOrDefault(ref position, defaultValue: defaultVersion);
Console.WriteLine($"Parsed: {parsed}"); // 1.0.0.0
Console.WriteLine($"Position: {position}"); // 0 (not advanced on failure)
// Safe configuration parsing
byte[] configData = GetConfigurationData();
position = 0;
Version minVersion = configData.ToVersionOrDefault(ref position, 8, new Version(1, 0));
Version maxVersion = configData.ToVersionOrDefault(ref position, 8, new Version(99, 0));
// Backward compatibility handling
byte[] legacyFile = GetLegacyFile();
Version fileVersion = legacyFile.ToVersionOrDefault(4, 6, new Version(0, 9));
if (fileVersion < new Version(1, 0))
{
Console.WriteLine("Using legacy compatibility mode");
ProcessLegacyFormat(legacyFile);
}
else
{
ProcessModernFormat(legacyFile);
}
Enum Conversion
ToEnum
Converts byte arrays to strongly-typed enum values with comprehensive validation.
Usage Examples
// Define example enums
public enum FileType : byte
{
Unknown = 0,
Text = 1,
Binary = 2,
Image = 3,
Archive = 4
}
[Flags]
public enum Permissions : uint
{
None = 0,
Read = 1,
Write = 2,
Execute = 4,
Delete = 8,
ReadWrite = Read | Write,
FullControl = Read | Write | Execute | Delete
}
public enum Priority : short
{
Low = -1,
Normal = 0,
High = 1,
Critical = 2
}
// Basic enum conversion (1 byte enum)
byte[] fileData = { 3, 1, 2, 0 };
var position = 0;
FileType type1 = fileData.ToEnum<FileType>(ref position); // Image
FileType type2 = fileData.ToEnum<FileType>(ref position); // Text
FileType type3 = fileData.ToEnum<FileType>(ref position); // Binary
FileType type4 = fileData.ToEnum<FileType>(ref position); // Unknown
Console.WriteLine($"Position: {position}"); // 4
// Fixed position access
FileType specificType = fileData.ToEnum<FileType>(2); // Binary
// Flags enum conversion (4 bytes)
byte[] permData = { 0x0F, 0x00, 0x00, 0x00 }; // 15 = Read|Write|Execute|Delete
Permissions perms = permData.ToEnum<Permissions>(0); // FullControl
Console.WriteLine($"Has Read: {perms.HasFlag(Permissions.Read)}"); // True
Console.WriteLine($"Has Write: {perms.HasFlag(Permissions.Write)}"); // True
// Different underlying types
byte[] priorityData = { 0xFF, 0xFF, 0x01, 0x00 }; // -1 as short, then 1
position = 0;
Priority lowPriority = priorityData.ToEnum<Priority>(ref position); // Low (-1)
Priority highPriority = priorityData.ToEnum<Priority>(ref position); // High (1)
// Protocol message parsing
byte[] messageData = ReceiveMessage();
position = 0;
byte messageLength = messageData.ToByte(ref position);
MessageType msgType = messageData.ToEnum<MessageType>(ref position);
Priority msgPriority = messageData.ToEnum<Priority>(ref position);
// File format enum parsing
byte[] formatData = GetFileFormatData();
position = 0;
CompressionType compression = formatData.ToEnum<CompressionType>(ref position);
EncryptionType encryption = formatData.ToEnum<EncryptionType>(ref position);
// Validation ensures only valid enum values
try
{
byte[] invalidData = { 99 }; // Not a valid FileType
FileType invalid = invalidData.ToEnum<FileType>(0); // Throws ArgumentException
}
catch (ArgumentException ex)
{
Console.WriteLine($"Invalid enum value: {ex.Message}");
}
ToEnumOrDefault
Safe enum conversion with default value fallback for invalid data.
Usage Examples
byte[] invalidData = { 99 }; // Invalid FileType value
var position = 0;
// Safe conversion with default
FileType safeType = invalidData.ToEnumOrDefault<FileType>(ref position, FileType.Unknown);
Console.WriteLine($"Type: {safeType}"); // Unknown
Console.WriteLine($"Position: {position}"); // 0 (not advanced on failure)
// Safe protocol parsing
byte[] protocolData = GetProtocolData();
position = 0;
MessageType msgType = protocolData.ToEnumOrDefault<MessageType>(ref position, MessageType.Heartbeat);
Priority priority = protocolData.ToEnumOrDefault<Priority>(ref position, Priority.Normal);
// Graceful degradation for unknown values
byte[] featureFlags = GetFeatureFlags();
position = 0;
while (position < featureFlags.Length - 3) // Ensure 4 bytes for uint enum
{
FeatureFlag flag = featureFlags.ToEnumOrDefault<FeatureFlag>(ref position, FeatureFlag.None);
if (flag != FeatureFlag.None)
{
EnableFeature(flag);
}
}
// Backward compatibility with new enum values
byte[] futureData = GetDataFromNewerVersion();
position = 0;
NewFeature feature = futureData.ToEnumOrDefault<NewFeature>(ref position, NewFeature.LegacyMode);
if (feature == NewFeature.LegacyMode)
{
Console.WriteLine("Unknown feature, using legacy mode");
}
Advanced Usage Patterns
Mixed Complex Types in Protocols
byte[] protocolHeader = ReceiveHeader();
var position = 0;
// Parse complex protocol header
Version protocolVersion = protocolHeader.ToVersion(ref position, 8);
MessageType messageType = protocolHeader.ToEnum<MessageType>(ref position);
Permissions requiredPerms = protocolHeader.ToEnum<Permissions>(ref position);
Priority messagePriority = protocolHeader.ToEnum<Priority>(ref position);
Console.WriteLine($"Protocol {protocolVersion}, Type: {messageType}, Perms: {requiredPerms}, Priority: {messagePriority}");
Flags Enum Validation
[Flags]
public enum AccessRights : byte
{
None = 0,
Read = 1,
Write = 2,
Execute = 4,
Admin = 8
}
byte[] accessData = { 0x0F }; // All flags set
AccessRights rights = accessData.ToEnum<AccessRights>(0);
// Check individual flags
if (rights.HasFlag(AccessRights.Read))
Console.WriteLine("Has read access");
if (rights.HasFlag(AccessRights.Write))
Console.WriteLine("Has write access");
// Invalid flags combination (bit 5 set, not defined)
try
{
byte[] invalidFlags = { 0x20 }; // Bit 5 set (32), not defined in enum
AccessRights invalid = invalidFlags.ToEnum<AccessRights>(0); // Throws
}
catch (ArgumentException)
{
Console.WriteLine("Invalid flags combination detected");
}
Version Comparison and Compatibility
byte[] versionInfo = GetVersionInfo();
var position = 0;
Version currentVersion = versionInfo.ToVersion(ref position, 6);
Version minimumVersion = versionInfo.ToVersion(ref position, 6);
Version maximumVersion = versionInfo.ToVersion(ref position, 6);
// Version compatibility checks
Version myVersion = new Version(2, 1, 0);
if (myVersion < minimumVersion)
{
throw new InvalidOperationException($"Version {myVersion} is too old. Minimum: {minimumVersion}");
}
if (myVersion > maximumVersion)
{
Console.WriteLine($"Warning: Version {myVersion} is newer than tested version {maximumVersion}");
}
// Feature availability based on version
if (currentVersion >= new Version(2, 0))
{
EnableAdvancedFeatures();
}
if (currentVersion.Major >= 3)
{
EnableNextGenFeatures();
}
Safe Complex Data Parsing
byte[] complexData = GetComplexData();
var results = new List<DataRecord>();
var position = 0;
while (position < complexData.Length)
{
// Try to parse each record safely
var record = new DataRecord();
record.Version = complexData.ToVersionOrDefault(ref position, 8, new Version(1, 0));
record.Type = complexData.ToEnumOrDefault<RecordType>(ref position, RecordType.Unknown);
record.Status = complexData.ToEnumOrDefault<Status>(ref position, Status.Pending);
// Skip record if parsing failed
if (record.Type == RecordType.Unknown && record.Status == Status.Pending)
{
position++; // Skip one byte and try again
continue;
}
results.Add(record);
}
Console.WriteLine($"Successfully parsed {results.Count} records");
Type Safety Features
Compile-Time Type Safety
// Generic constraint ensures only enums can be used
public static void ProcessEnum<T>(byte[] data) where T : Enum
{
T value = data.ToEnum<T>(0); // Compile-time safe
Console.WriteLine($"Processed {typeof(T).Name}: {value}");
}
// Usage
ProcessEnum<FileType>(fileData);
ProcessEnum<Priority>(priorityData);
// ProcessEnum<string>(stringData); // Compile error - string is not an enum
Runtime Validation
// Enum validation catches:
// 1. Out-of-range values for regular enums
// 2. Invalid flag combinations for [Flags] enums
// 3. Insufficient data for the underlying type
public enum Color : int { Red = 1, Green = 2, Blue = 3 }
byte[] colorData = { 0x04, 0x00, 0x00, 0x00 }; // 4 (invalid)
try
{
Color color = colorData.ToEnum<Color>(0); // Throws ArgumentException
}
catch (ArgumentException ex)
{
Console.WriteLine($"Invalid color value: {ex.Message}");
// Message includes valid values: "Valid values are: Red, Green, Blue"
}
Performance Characteristics
Operation | Performance | Notes |
---|---|---|
ToVersion |
Moderate | UTF-8 parsing + Version.Parse |
ToEnum<T> (byte) |
Fast | Direct cast + validation |
ToEnum<T> (larger) |
Fast | BitConverter + validation |
OrDefault variants |
Fast | Exception-free validation |
Best Practices
- Use specific enum types rather than generic integer conversions for type safety
- Validate enum values using the built-in validation to catch data corruption
- Handle version compatibility with proper comparison logic
- Use OrDefault variants for robust parsing of potentially corrupted data
- Define meaningful default values for OrDefault methods
- Document enum underlying types to ensure correct byte array sizing
Common Use Cases
- Protocol Messages: Type-safe parsing of message types and flags
- File Formats: Reading format versions and type indicators
- Configuration Files: Safe parsing of settings and feature flags
- Network Communication: Protocol version negotiation
- Data Serialization: Type-safe enum storage and retrieval
- Legacy System Integration: Handling unknown enum values gracefully
- Version Management: Application and data format versioning
See Also
- ByteArrayExtensions - For underlying type conversions and Version string parsing
- ByteArrayBuilder - For constructing byte arrays from complex types
- ObjectToByteArrayExtensions - For object serialization