-
-
Notifications
You must be signed in to change notification settings - Fork 362
feat(ISocketDataPropertyConverter): add ISocketDataPropertyConverter interface #6422
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
* feat: 增加数据转换器设计 * refactor: 数据处理器增加数据转化器适配 * test: 增加单元测试 * refactor: 更新判断转换成功逻辑
Reviewer's GuideThis PR introduces a comprehensive socket data conversion framework based on attributes and pluggable converters, extends the existing data package adapter and client extensions to support generic entity conversion via ISocketDataConverter, and adds extensive unit tests for both the converter implementations and the integration with the TCP socket client. Class diagram for the new socket data conversion frameworkclassDiagram
class ISocketDataPropertyConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class ISocketDataConverter~TEntity~ {
+bool TryConvertTo(ReadOnlyMemory<byte> data, out TEntity? entity)
}
class SocketDataConverterBase~TEntity~ {
+bool TryConvertTo(ReadOnlyMemory<byte> data, out TEntity? entity)
#bool Parse(ReadOnlyMemory<byte> data, TEntity entity)
}
class SocketDataConverterAttribute {
+Type? Type
}
class SocketDataPropertyAttribute {
+Type? Type
+int Offset
+int Length
+string? EncodingName
+Type? ConverterType
+object?[]? ConverterParameters
}
class SocketDataPropertyExtensions {
+ISocketDataPropertyConverter? GetConverter(SocketDataPropertyAttribute)
+object? ConvertTo(SocketDataPropertyAttribute, ReadOnlyMemory<byte> data)
}
ISocketDataConverter~TEntity~ <|-- SocketDataConverterBase~TEntity~
SocketDataPropertyAttribute <.. SocketDataPropertyExtensions
SocketDataPropertyAttribute <.. ISocketDataPropertyConverter
SocketDataConverterAttribute <.. ISocketDataConverter~TEntity~
%% Concrete converters
class SocketDataByteArrayConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataStringConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataEnumConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataBoolConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataInt16BigEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataInt16LittleEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataInt32BigEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataInt32LittleEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataInt64BigEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataInt64LittleEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataSingleBigEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataSingleLittleEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataDoubleBigEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataDoubleLittleEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataUInt16BigEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataUInt16LittleEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataUInt32BigEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataUInt32LittleEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataUInt64BigEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
class SocketDataUInt64LittleEndianConverter {
+object? Convert(ReadOnlyMemory<byte> data)
}
ISocketDataPropertyConverter <|.. SocketDataByteArrayConverter
ISocketDataPropertyConverter <|.. SocketDataStringConverter
ISocketDataPropertyConverter <|.. SocketDataEnumConverter
ISocketDataPropertyConverter <|.. SocketDataBoolConverter
ISocketDataPropertyConverter <|.. SocketDataInt16BigEndianConverter
ISocketDataPropertyConverter <|.. SocketDataInt16LittleEndianConverter
ISocketDataPropertyConverter <|.. SocketDataInt32BigEndianConverter
ISocketDataPropertyConverter <|.. SocketDataInt32LittleEndianConverter
ISocketDataPropertyConverter <|.. SocketDataInt64BigEndianConverter
ISocketDataPropertyConverter <|.. SocketDataInt64LittleEndianConverter
ISocketDataPropertyConverter <|.. SocketDataSingleBigEndianConverter
ISocketDataPropertyConverter <|.. SocketDataSingleLittleEndianConverter
ISocketDataPropertyConverter <|.. SocketDataDoubleBigEndianConverter
ISocketDataPropertyConverter <|.. SocketDataDoubleLittleEndianConverter
ISocketDataPropertyConverter <|.. SocketDataUInt16BigEndianConverter
ISocketDataPropertyConverter <|.. SocketDataUInt16LittleEndianConverter
ISocketDataPropertyConverter <|.. SocketDataUInt32BigEndianConverter
ISocketDataPropertyConverter <|.. SocketDataUInt32LittleEndianConverter
ISocketDataPropertyConverter <|.. SocketDataUInt64BigEndianConverter
ISocketDataPropertyConverter <|.. SocketDataUInt64LittleEndianConverter
Class diagram for updated IDataPackageAdapter and ITcpSocketClientExtensionsclassDiagram
class IDataPackageAdapter {
+ValueTask HandlerAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
+bool TryConvertTo~TEntity~(ReadOnlyMemory<byte> data, ISocketDataConverter~TEntity~ socketDataConverter, out TEntity? entity)
}
class ITcpSocketClient {
+Func<ReadOnlyMemory<byte>, Task> ReceivedCallBack
}
class ITcpSocketClientExtensions {
+void SetDataPackageAdapter~TEntity~(ITcpSocketClient client, IDataPackageAdapter adapter, ISocketDataConverter~TEntity~ socketDataConverter, Func<TEntity?, Task> callback)
+void SetDataPackageAdapter~TEntity~(ITcpSocketClient client, IDataPackageAdapter adapter, Func<TEntity?, Task> callback)
}
IDataPackageAdapter <.. ITcpSocketClientExtensions : uses
ITcpSocketClient <.. ITcpSocketClientExtensions : configures
ISocketDataConverter~TEntity~ <.. IDataPackageAdapter : uses
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @ArgoZhang - I've reviewed your changes - here's some feedback:
- You’ve changed IDataPackageAdapter.TryConvertTo to a generic overload which will break existing non-generic adapters—consider providing a backward-compatible shim or overload.
- The reflection-based SetDataPackageAdapter overload does not handle converter instantiation failures or type mismatches—add error checking or logging to surface misconfigurations.
- Your default property converters use big-endian but tests cover little-endian converters—please clarify endianness semantics or offer a configurable default byte order.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- You’ve changed IDataPackageAdapter.TryConvertTo to a generic overload which will break existing non-generic adapters—consider providing a backward-compatible shim or overload.
- The reflection-based SetDataPackageAdapter overload does not handle converter instantiation failures or type mismatches—add error checking or logging to surface misconfigurations.
- Your default property converters use big-endian but tests cover little-endian converters—please clarify endianness semantics or offer a configurable default byte order.
## Individual Comments
### Comment 1
<location> `src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs:95` </location>
<code_context>
+ var start = attribute.Offset;
+ var length = attribute.Length;
+
+ if (data.Length >= start + length)
+ {
+ var buffer = data.Slice(start, length);
</code_context>
<issue_to_address>
Potential for out-of-bounds slice if Offset or Length are negative.
Please add checks to ensure both `start` and `length` are non-negative before slicing.
</issue_to_address>
### Comment 2
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataEnumConverter.cs:22` </location>
<code_context>
+ public object? Convert(ReadOnlyMemory<byte> data)
+ {
+ var ret = false;
+ if (data.Length == 1)
+ {
+ ret = data.Span[0] != 0x00;
</code_context>
<issue_to_address>
Enum conversion only supports single-byte values.
This implementation assumes enums fit in a single byte. Please add support for larger underlying types or clearly document this limitation.
</issue_to_address>
### Comment 3
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32BigEndianConverter.cs:23` </location>
<code_context>
+ {
+ var ret = 0;
+ Span<byte> paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+
+ if (BinaryPrimitives.TryReadInt32BigEndian(paddedSpan, out var v))
</code_context>
<issue_to_address>
No check for data length greater than 4 bytes.
Add a check to ensure `data.Length` is 4 or less to prevent exceptions during the slice operation.
</issue_to_address>
### Comment 4
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32LittleEndianConverter.cs:23` </location>
<code_context>
+ {
+ var ret = 0;
+ Span<byte> paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+
+ if (BinaryPrimitives.TryReadInt32BigEndian(paddedSpan, out var v))
</code_context>
<issue_to_address>
No check for data length greater than 4 bytes.
Add a check to ensure `data.Length` is 4 or less before performing the slice to prevent exceptions.
</issue_to_address>
### Comment 5
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64BigEndianConverter.cs:23` </location>
<code_context>
+ {
+ double ret = 0;
+ Span<byte> paddedSpan = stackalloc byte[8];
+ data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
+ if (BinaryPrimitives.TryReadDoubleBigEndian(paddedSpan, out var v))
+ {
</code_context>
<issue_to_address>
No check for data length greater than 8 bytes.
Add a check to prevent `data.Length` from exceeding 8 to avoid exceptions during the slice operation.
</issue_to_address>
### Comment 6
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleBigEndianConverter.cs:23` </location>
<code_context>
+ {
+ var ret = 0;
+ Span<byte> paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+
+ if (BinaryPrimitives.TryReadInt32BigEndian(paddedSpan, out var v))
</code_context>
<issue_to_address>
No check for data length greater than 4 bytes.
Add a check to ensure `data.Length` is not greater than 4 to prevent exceptions during the slice operation.
</issue_to_address>
### Comment 7
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleLittleEndianConverter.cs:23` </location>
<code_context>
+ {
+ var ret = 0;
+ Span<byte> paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+
+ if (BinaryPrimitives.TryReadInt32BigEndian(paddedSpan, out var v))
</code_context>
<issue_to_address>
No check for data length greater than 4 bytes.
Add a check to ensure `data.Length` is not greater than 4 to prevent exceptions during the slice operation.
</issue_to_address>
### Comment 8
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16BigEndianConverter.cs:23` </location>
<code_context>
+ {
+ short ret = 0;
+ Span<byte> paddedSpan = stackalloc byte[2];
+ data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
+ if (BinaryPrimitives.TryReadInt16BigEndian(paddedSpan, out var v))
+ {
</code_context>
<issue_to_address>
No check for data length greater than 2 bytes.
Add a check to ensure `data.Length` is not greater than 2 to prevent exceptions during the slice operation.
</issue_to_address>
### Comment 9
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16LittleEndianConverter.cs:23` </location>
<code_context>
+ {
+ short ret = 0;
+ Span<byte> paddedSpan = stackalloc byte[2];
+ data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
+ if (BinaryPrimitives.TryReadInt16BigEndian(paddedSpan, out var v))
+ {
</code_context>
<issue_to_address>
No check for data length greater than 2 bytes.
Add a check to ensure `data.Length` is not greater than 2 to prevent exceptions during the slice operation.
</issue_to_address>
### Comment 10
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32BigEndianConverter.cs:23` </location>
<code_context>
+ {
+ var ret = 0;
+ Span<byte> paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+
+ if (BinaryPrimitives.TryReadInt32BigEndian(paddedSpan, out var v))
</code_context>
<issue_to_address>
No check for data length greater than 4 bytes.
Add a check to ensure `data.Length` is not greater than 4 to prevent exceptions during the slice operation.
</issue_to_address>
### Comment 11
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32LittleEndianConverter.cs:23` </location>
<code_context>
+ {
+ var ret = 0;
+ Span<byte> paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+
+ if (BinaryPrimitives.TryReadInt32BigEndian(paddedSpan, out var v))
</code_context>
<issue_to_address>
No check for data length greater than 4 bytes.
Add a check to ensure `data.Length` is not greater than 4 to prevent exceptions during the slice operation.
</issue_to_address>
### Comment 12
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64BigEndianConverter.cs:23` </location>
<code_context>
+ {
+ double ret = 0;
+ Span<byte> paddedSpan = stackalloc byte[8];
+ data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
+ if (BinaryPrimitives.TryReadDoubleBigEndian(paddedSpan, out var v))
+ {
</code_context>
<issue_to_address>
No check for data length greater than 8 bytes.
Add a check to prevent `data.Length` from exceeding 8 to avoid exceptions during the slice operation.
</issue_to_address>
### Comment 13
<location> `src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64LittleEndianConverter.cs:23` </location>
<code_context>
+ {
+ double ret = 0;
+ Span<byte> paddedSpan = stackalloc byte[8];
+ data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
+ if (BinaryPrimitives.TryReadDoubleBigEndian(paddedSpan, out var v))
+ {
</code_context>
<issue_to_address>
No check for data length greater than 8 bytes.
Add a check to prevent `data.Length` from exceeding 8 to avoid exceptions during the slice operation.
</issue_to_address>
### Comment 14
<location> `test/UnitTest/Services/TcpSocketFactoryTest.cs:735` </location>
<code_context>
- // 发送数据
- var data = new ReadOnlyMemory<byte>([1, 2, 3, 4, 5]);
+ // 测试数据适配器直接调用 TryConvertTo 方法转换数据
+ var adapter2 = new DataPackageAdapter();
+ var result = adapter2.TryConvertTo(data, new MockEntitySocketDataConverter(), out var t);
+ Assert.True(result);
+ Assert.NotNull(t);
+ Assert.Equal([1, 2, 3, 4, 5], entity.Header);
+
</code_context>
<issue_to_address>
Consider adding negative test cases for TryConvertTo.
Please add tests with invalid or incomplete input to verify that TryConvertTo returns false and the output entity is null.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
// 测试数据适配器直接调用 TryConvertTo 方法转换数据
var adapter2 = new DataPackageAdapter();
var result = adapter2.TryConvertTo(data, new MockEntitySocketDataConverter(), out var t);
Assert.True(result);
Assert.NotNull(t);
Assert.Equal([1, 2, 3, 4, 5], entity.Header);
=======
// 测试数据适配器直接调用 TryConvertTo 方法转换数据
var adapter2 = new DataPackageAdapter();
var result = adapter2.TryConvertTo(data, new MockEntitySocketDataConverter(), out var t);
Assert.True(result);
Assert.NotNull(t);
Assert.Equal([1, 2, 3, 4, 5], entity.Header);
// Negative test cases for TryConvertTo
// Case 1: Empty data
var emptyData = new ReadOnlyMemory<byte>([]);
var negativeResult1 = adapter2.TryConvertTo(emptyData, new MockEntitySocketDataConverter(), out var negativeEntity1);
Assert.False(negativeResult1);
Assert.Null(negativeEntity1);
// Case 2: Incomplete data (e.g., only 2 bytes)
var shortData = new ReadOnlyMemory<byte>([1, 2]);
var negativeResult2 = adapter2.TryConvertTo(shortData, new MockEntitySocketDataConverter(), out var negativeEntity2);
Assert.False(negativeResult2);
Assert.Null(negativeEntity2);
// Case 3: Null data (if TryConvertTo supports nullable, otherwise skip)
// Uncomment the following lines if TryConvertTo accepts null as input
// ReadOnlyMemory<byte>? nullData = null;
// var negativeResult3 = adapter2.TryConvertTo(nullData, new MockEntitySocketDataConverter(), out var negativeEntity3);
// Assert.False(negativeResult3);
// Assert.Null(negativeEntity3);
>>>>>>> REPLACE
</suggested_fix>
### Comment 15
<location> `test/UnitTest/Services/TcpSocketFactoryTest.cs:710` </location>
<code_context>
+ // bool
+ Assert.True(entity.Value10);
+
+ // enum
+ Assert.Equal(EnumEducation.Middle, entity.Value11);
+
+ // foo
</code_context>
<issue_to_address>
Consider adding tests for invalid enum values.
Please include a test for when the enum value is invalid to verify proper error handling.
Suggested implementation:
```csharp
// enum
Assert.Equal(EnumEducation.Middle, entity.Value11);
// invalid enum value
// Simulate deserialization or assignment of an invalid enum value
// This assumes Value11 is settable for test purposes; adjust as needed for your codebase.
Assert.Throws<ArgumentException>(() => entity.Value11 = (EnumEducation)999);
// foo
Assert.NotNull(entity.Value12);
```
If `entity.Value11` is not settable, or if the enum value is set via deserialization, you may need to simulate deserialization with an invalid value and assert the expected error handling (e.g., exception thrown, default value set, etc.). Adjust the test accordingly to fit your codebase's actual error handling for invalid enum values.
</issue_to_address>
### Comment 16
<location> `test/UnitTest/Services/TcpSocketFactoryTest.cs:689` </location>
<code_context>
+ // long
+ Assert.Equal(16, entity.Value3);
+
+ // double
+ Assert.Equal(3.14, entity.Value4);
+
+ // single
</code_context>
<issue_to_address>
Add tests for boundary and invalid floating-point values.
Please include tests for NaN, Infinity, and extreme float/double values to verify correct converter behavior.
Suggested implementation:
```csharp
// double
Assert.Equal(3.14, entity.Value4);
// double boundary and invalid values
Assert.True(double.IsNaN(entity.NaNDouble));
Assert.True(double.IsPositiveInfinity(entity.PositiveInfinityDouble));
Assert.True(double.IsNegativeInfinity(entity.NegativeInfinityDouble));
Assert.Equal(double.MaxValue, entity.MaxDouble);
Assert.Equal(double.MinValue, entity.MinDouble);
// single
Assert.NotEqual(0, entity.Value5);
// single boundary and invalid values
Assert.True(float.IsNaN(entity.NaNSingle));
Assert.True(float.IsPositiveInfinity(entity.PositiveInfinitySingle));
Assert.True(float.IsNegativeInfinity(entity.NegativeInfinitySingle));
Assert.Equal(float.MaxValue, entity.MaxSingle);
Assert.Equal(float.MinValue, entity.MinSingle);
```
You will need to ensure that the `entity` object has the following properties (or similar) for these tests to compile and pass:
- NaNDouble, PositiveInfinityDouble, NegativeInfinityDouble, MaxDouble, MinDouble (of type double)
- NaNSingle, PositiveInfinitySingle, NegativeInfinitySingle, MaxSingle, MinSingle (of type float)
If these properties do not exist, you will need to add them to the entity and ensure they are set up appropriately in your test setup.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs
Show resolved
Hide resolved
src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataEnumConverter.cs
Show resolved
Hide resolved
src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32BigEndianConverter.cs
Show resolved
Hide resolved
...BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32LittleEndianConverter.cs
Show resolved
Hide resolved
src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64BigEndianConverter.cs
Show resolved
Hide resolved
src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64BigEndianConverter.cs
Show resolved
Hide resolved
...ootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64LittleEndianConverter.cs
Show resolved
Hide resolved
| // enum | ||
| Assert.Equal(EnumEducation.Middle, entity.Value11); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (testing): Consider adding tests for invalid enum values.
Please include a test for when the enum value is invalid to verify proper error handling.
Suggested implementation:
// enum
Assert.Equal(EnumEducation.Middle, entity.Value11);
// invalid enum value
// Simulate deserialization or assignment of an invalid enum value
// This assumes Value11 is settable for test purposes; adjust as needed for your codebase.
Assert.Throws<ArgumentException>(() => entity.Value11 = (EnumEducation)999);
// foo
Assert.NotNull(entity.Value12);If entity.Value11 is not settable, or if the enum value is set via deserialization, you may need to simulate deserialization with an invalid value and assert the expected error handling (e.g., exception thrown, default value set, etc.). Adjust the test accordingly to fit your codebase's actual error handling for invalid enum values.
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #6422 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 722 746 +24
Lines 31844 32177 +333
Branches 4492 4538 +46
==========================================
+ Hits 31844 32177 +333
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Link issues
fixes #6421
Summary By Copilot
Regression?
Risk
Verification
Packaging changes reviewed?
☑️ Self Check before Merge
Summary by Sourcery
Introduce a new socket data conversion framework by defining ISocketDataPropertyConverter and ISocketDataConverter interfaces, attribute-based property mapping, and default converters for common data types. Integrate the new converters into IDataPackageAdapter and ITcpSocketClientExtensions, and update tests to cover the new functionality.
New Features:
Enhancements:
Tests: