用vb14实现结构体的static_cast教程
vb.net吧
全部回复
仅看楼主
level 13
Nukepayload2 楼主
在某些情况下我们需要强制转换某些结构体,比如为了查看整数 -1 的补码,要把它强制转换成无符号整数。为了看某单精度浮点数的IEEE 754编码,要把它转换为无符号整数。
同时,又不想去碰c++,那么你找对教程了!
我想到了几种强制转换的方式。
1.利用联合体。
显式指定结构体的字段偏移量并且让某些字段在内存中发生重叠可以让结构体成为联合体。
假如你要查看-1的补码
先定义这个结构体
Imports System.Runtime.InteropServices
...
<StructLayout(LayoutKind.Explicit)>
Structure I4U4
  <FieldOffset(0)>
  Dim int As Integer
  <FieldOffset(0)>
  Dim uint As UInteger
End Structure
然后新建这个类型的变量并向它的int变量赋值,从uint变量取出来的就是原封不动的补码。
可以直接用这个类型的变量计算从而避免辅助空间。
时间复杂度:O(1)
空间复杂度:0 到 O(1)
编写成本:
每种情况要定义不同的结构体,然后在该转换的变量上多次使用成员运算符。
编写成本最好时略高于用c#的强制转换,最差情况下需要反复编写结构体代码。
2.使用BitConverter
先用GetBytes转换成Byte()再转换成目标类型
参考源码是这样写的
转换:
    public unsafe static byte[] GetBytes(int value)
    {
      Contract.Ensures(Contract.Result<byte[]>() != null);
      Contract.Ensures(Contract.Result<byte[]>().Length == 4)
      byte[] bytes = new byte[4];
      fixed(byte* b = bytes)
        *((int*)b) = value;
      return bytes;
    }
变回:
    public static unsafe int ToInt32 (byte[] value, int startIndex) {
      if( value == null) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
      }
if ((uint) startIndex >= value.Length) {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
      }
if (startIndex > value.Length -4) {
        ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
      }
      Contract.EndContractBlock();
      fixed( byte * pbyte = &value[startIndex]) {
        if( startIndex % 4 == 0) { // data is aligned
          return *((int *) pbyte);
        }
        else {
          if( IsLittleEndian) {
            return (*pbyte) | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24);
          }
          else {
            return (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3));
}
        }
      }
    }
时间复杂度:O(1)
空间复杂度:O(1)
编写成本:需要调用GetBytes()和Toxxxxx,灵活性不强,不能应对全部结构体。
因此编写成本低于或等于第一种。
3.使用MemoryStream
跟第二种差不多,写入内存流然后读出来。需要相应大小的数组作为辅助空间。
时间复杂度:O(1)
空间复杂度:O(1)
编写成本:需要辅助数组和内存流声明加上读写,同样不能应对全部的结构体,灵活性较差。
编写成本高于第二种低于等于第一种。
4.使用互操作函数
相当于把c#的不安全代码的转换翻译成vb。
时间复杂度:O(1)
空间复杂度:O(1)
编写成本:可以写成泛型函数或泛型类。调用时任何结构体都能轻易转换。
编写成本是很低的,比c#用指针转换写起来都省事。
5.使用支持指针的语言编写转换函数 (不推荐)
这明显是造轮子,写出来跟BitConverter效率差不多,为什么这样折腾呢?
时间复杂度:O(1)
空间复杂度:0
编写成本:调用与第4种方案类似。但是不能泛型。
编写成本大于等于第一种。
6.不做整数溢出检查并且使用CInt之类的运算符转换
在特殊情况比较好用,比如有符号与无符号的转换。但不能应对全部结构体。
时间复杂度:O(1)
空间复杂度:0
编写成本:跟c#的强制转换是一样的,有同样的适应性缺陷。
2015年11月28日 16点11分 1
level 12
[真棒]
2015年11月29日 01点11分 2
level 12
fieldoffest是啥
2015年11月29日 01点11分 3
指定字段在结构体中的位置
2015年11月29日 04点11分
@Nukepayload2 谢谢[哈哈]
2015年11月29日 12点11分
level 13
Nukepayload2 楼主
我想出了三种转换类或函数的写法,调用代码从长到短排列。
它们都基于我的PinnedPointer类。
Public Class StaticCast(Of T As Structure)
  Private Ptr As PinnedPointer(Of T)
  Sub New(Source As T)
    Ptr = New PinnedPointer(Of T)(Source)
  End Sub
  Public Function UnsafeCast(Of TDest As Structure)() As TDest
    Return Ptr.Cast(Of TDest).Target
  End Function
End Class
Public Class StaticCast(Of TSource As Structure, TDest As Structure)
  Private Ptr As PinnedPointer(Of TSource)
  Sub New(Source As TSource)
    Ptr = New PinnedPointer(Of TSource)(Source)
  End Sub
  Shared Widening Operator CType(Source As StaticCast(Of TSource, TDest)) As TDest
    Return Source.Ptr.Cast(Of TDest).Target
  End Operator
End Class
Public Module UnsafeCast
  <Extension>
  Public Function static_cast(Of TSource As Structure, TDest As Structure)(Source As TSource) As TDest
    Dim Ptr As New PinnedPointer(Of TSource)(Source)
    Return Ptr.Cast(Of TDest).Target
  End Function
End Module
Class usage
  Sub test()
    Dim i = -1
    Dim ui = New StaticCast(Of Integer)(i).UnsafeCast(Of UInteger)
    Dim ui2 As UInteger = New StaticCast(Of Integer, UInteger)(i)
    Dim ui3 = i.static_cast(Of UInteger)
  End Sub
End Class
2015年11月29日 04点11分 4
level 12
好顶赞!也可以用int指针来读uint的地址吧
2015年11月29日 07点11分 5
这是没有任何限制的
2015年11月29日 08点11分
我认为最快的办法是用联合体,因为指针的空间也不用申请了。
2015年11月29日 08点11分
回复 Nukepayload2 :嗯嗯
2015年11月29日 10点11分
1