Arrays 如何将在具有约束结构的泛型类中获得的字节数组作为泛型值返回?

Arrays 如何将在具有约束结构的泛型类中获得的字节数组作为泛型值返回?,arrays,.net,vb.net,generics,Arrays,.net,Vb.net,Generics,我在返回泛型时遇到一些困难 我有一个名为Scalar的类。其目的是仅处理数值。显然,如果不抛出运行时异常,这是不可执行的。我能做的最好的事情就是使用结构约束。到目前为止,还可以接受 在类中,我使用一个包含表示此类泛型的字节的流。这些可以是Byte、Int32、UInt64等类型的序列。它们都是相同的数据类型,并且表示数字 考虑这个类中的一个函数Item,我在其中调用一个函数,该函数从该流中读取数据。结果存储在字节数组中。我知道数组是绑定的,因为在构造时使用Marshal.SizeOf对其进行计算

我在返回泛型时遇到一些困难

我有一个名为
Scalar
的类。其目的是仅处理数值。显然,如果不抛出运行时异常,这是不可执行的。我能做的最好的事情就是使用
结构
约束。到目前为止,还可以接受

在类中,我使用一个包含表示此类泛型的字节的流。这些可以是Byte、Int32、UInt64等类型的序列。它们都是相同的数据类型,并且表示数字

考虑这个类中的一个函数
Item
,我在其中调用一个函数,该函数从该流中读取数据。结果存储在字节数组中。我知道数组是绑定的,因为在构造时使用Marshal.SizeOf对其进行计算和存储。然而,我很难将这个字节数组作为数据类型
T
返回

'SizeOf:
Imports System.Runtime.InteropServices.Marshal

Public Class Scalar(Of T As Structure)
    Private gtValue As T
    Private giSize As Int32

    Public Sub New(Value As T)
        gtValue = Value
        giSize = SizeOf(gtValue)
    End Sub

    Public Function Value() As T
        Return gtValue
    End Function

    Public Function Item() As T
        'The return value stems from a stream. It is packed into a bytes array
        'of appropriate size (8 B for Int64, Double, 1 B for Byte etc.). The
        'Byte array is in little endian order.
        Dim abItem(0 To giSize - 1) As Byte     'Result of Stream function.

        'How do I return abItem as T?
    End Function
End Class
当然,我可以遍历数组,逐字节组合数字本身,然后将其作为非泛型数据类型返回,例如作为
Int64

    Dim iElement As Int64 = 0
    For i = 0 To giIndexLenBytes - 1
        'Shift prior content 8 bits to the left and add new (unsigned) byte.
        iElement <<= 8
        iElement += abItem(i)
    Next
    Return iElement
显然,我可以通过几种方式获得数字


但是,是否有可能将一次获得的数字分配给可返回的
T
变量,以便泛型类对它的用户有用?

这可能不是最有效的方法,但它是安全的,不会诉诸任何疯狂的手段:

Public Function CreateStructureFromByteArray(Of T As Structure)(bytes() As Byte) As T
    Dim arrayPointer As IntPtr = Marshal.AllocHGlobal(bytes.Length)
    Dim result As T = Nothing
    Try
        Marshal.Copy(bytes, 0, arrayPointer, bytes.Length)
        result = Marshal.PtrToStructure(Of T)(arrayPointer)
    Finally
        Marshal.FreeHGlobal(arrayPointer)
    End Try
    Return result
End Function
请注意,这里的假设是,输入字节数组包含使用与当前系统相同的endianness的数据。如果不是这样,则需要在调用
Marshal.PtrToStructure
之前反转数组


另一方面,我曾想过尝试使用具有显式布局的结构来进行转换,如下所示:

<StructLayout(LayoutKind.Explicit)>
Structure EvilUnion(Of T As Structure)
    <FieldOffset(0)> Public Byte1 As Byte
    <FieldOffset(1)> Public Byte2 As Byte
    <FieldOffset(2)> Public Byte3 As Byte
    <FieldOffset(3)> Public Byte4 As Byte
    <FieldOffset(0)> Public Struct As T
End Structure

结构(T的As结构)
公共字节1作为字节
公共字节2作为字节
公共字节3作为字节
公共字节4作为字节
公共结构
端部结构
(由于数组是引用类型,因此每个字节都必须有单独的字段,就像这样,这将是一个难题。
CreateStructureFromByteArray
函数中的代码不仅需要单独设置每个字节字段的值,而且还必须检查以确保泛型类型不超过4,或者无论您在其中输入了多少字节字段。)


这可能是值得的,只是为了更好的性能,但不幸的是(也不足为奇)您不能在泛型结构上使用
StructLayoutAttribute
。此时,您将被迫为每种类型创建一个单独的显式布局结构,然后您又回到了原来的问题上。

这是一种有效的方法,但它使用了未记录的关键字。它是
c
。抱歉:(

<StructLayout(LayoutKind.Explicit)>
Structure EvilUnion(Of T As Structure)
    <FieldOffset(0)> Public Byte1 As Byte
    <FieldOffset(1)> Public Byte2 As Byte
    <FieldOffset(2)> Public Byte3 As Byte
    <FieldOffset(3)> Public Byte4 As Byte
    <FieldOffset(0)> Public Struct As T
End Structure
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TOut FromBytes<TOut>(byte[] bytes) where TOut : struct
{
    unsafe
    {
        TOut result = default(TOut);

        TypedReference resultRef = __makeref(result);
        byte* resultPtr = (byte*)*((IntPtr*)&resultRef);

        TypedReference curValueRef = __makeref(bytes[0]);
        byte* curValuePtr = (byte*)*((IntPtr*)&curValueRef);

        for (int i = 0; i < Marshal.SizeOf<TOut>(); ++i)
        {
            resultPtr[i] = curValuePtr[i];
        }

        return result;
    }
}
public static void Main()
{
    var tests = new List<byte[]>
    {
        new byte[] { 1, 0, 0, 0 },
        new byte[] { 2, 0, 0, 0 },
        new byte[] { 0, 1, 0, 0 },
        new byte[] { 0, 2, 0, 0 },
        new byte[] { 0, 0, 1, 0 },
        new byte[] { 0, 0, 2, 0 },
    };
    foreach (var bytes in tests)
    {
        int integer = FromBytes<int>(bytes);
        long longInteger = FromBytes<long>(bytes);

        Console.WriteLine("{0}...{1}", integer, longInteger);
    }
}
1...1
2...2
256...256
512...512
65536...65536
131072...131072