首页 > 解决方案 > 如何返回在具有约束结构的泛型类中获得的字节数组作为泛型值?

问题描述

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

我有一个名为Scalar. 它的目的是只处理数值。显然,如果不抛出运行时异常,这并不是真正可执行的。我能做的最好的就是使用Structure约束。到目前为止,还可以接受。

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

考虑Item这个类中的一个函数,我在其中调用一个函数,该函数从该流中读取。结果存储在字节数组中。我知道数组的界限是因为在构建时使用 Marshal.SizeOf 评估和存储它。但是,我正在努力将此字节数组作为 datatype 返回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

我也可以使用BitConverterand 一个Select块来获取数字,但这会更加笨拙,最后也没有一点帮助:

    Dim iElement As Int64
    Select Case gtValue.GetType.ToString
        Case "Byte"
            iElement = abItem(0)
        Case "Integer"
            iElement = BitConverter.ToInt32(abItem, 0)
        Case "Long"
            iElement = BitConverter.ToInt64(abItem, 0)

            'etc...
    End Select

显然,我可以通过多种方式获得该号码。

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

标签: arrays.netvb.netgenerics

解决方案


这可能不是最有效的方法,但它是安全的,无需采取任何疯狂的措施:

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

请注意,这里的假设是输入字节数组包含使用与当前系统相同的字节序的数据。如果不是这种情况,您需要在调用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

(由于数组是引用类型,你必须为每个字节有单独的字段,就像那​​样,这会很痛苦。你的CreateStructureFromByteArray函数中的代码不仅需要单独设置每个字节字段的值,而且它会还必须检查以确保泛型类型不超过 4,或者您放入的字节字段不超过多少。)

这可能是值得的,只是为了更好的性能,但不幸的是(并且不出所料)你不能StructLayoutAttribute在通用结构上使用。那时,您将被迫为每种类型创建一个单独的显式布局结构,然后您又回到了最初的问题。


推荐阅读