C#使用StructLayout特性来控制内存结构的操作代码

 更新时间:2024年10月15日 09:05:36   作者:△曉風殘月〆  
在C#中,内存布局对于性能优化和与非托管代码的互操作性至关重要,StructLayout特性允许开发者控制结构在内存中的布局方式,本文给大家介绍了C#使用StructLayout特性来控制内存结构的操作,需要的朋友可以参考下

C#在调用WInAPI函数时,可能会看到如下的声明

[StructLayout(LayoutKind.Sequential)]
         public struct RECT
         {
             public int Left;
             public int Top;
             public int Right;
             public int Bottom;
         }

在类或者结构体前面带上了

[StructLayout(LayoutKind.Sequential)]

StructLayoutAttribute特性的作用是允许你控制内存中类或结构的数据字段的物理布局。

平常我们在C#代码中使用类或者结构体时,不需要使用此特性。但在与非托管代码时交互,需要使用StructLayoutAttribute特性来控制类型的非托管布局。

StructLayoutAttribute常用构造函数是:

StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind)

System.Runtime.InteropServices.LayoutKind是一个枚举类型,有三个取值。

LayoutKind.Sequential

强制按成员的显示顺序对其进行排列。对于blittable类型,在托管和非托管内存中控制布局。对于non-blittable类型,它会在将类或者结构体封送到非托管代码时控制布局(换言之,如果仅在C#中进行调用,不会做任何操作,在与非托管代码交互时,仅控制送入非托管代码的布局)

LayoutKind.Explicit

控制每个数据成员的精确位置,这会影响托管和非托管代码中的布局,不管是blittable类型还是non-blittable类型。,使用LayoutKind.Explicit时,需要使用FieldOffsetAttribute特性指示类型中每个字段的位置。

默认情况下,编译器会将LayoutKind.Sequential应用到结构体,对于类,需要显式应用LayoutKind.Sequential值。

到这里也就明白了,以后在调用API函数时,如果使用的是结构体,就不再需要下面这句代码了。

[StructLayout(LayoutKind.Sequential)]

下面用示例代码说明一下

这里以获取桌面窗体的宽高为例,需要用到的API函数是

HWND GetDesktopWindow();

BOOL GetWindowRect(HWND hWnd, LPRECT lpRect );

其中LPRECT是指向RECT结构体的指针,RECT结构声明如下:

typedef struct tagRECT {
   LONG left;
   LONG top;
   LONG right;
   LONG bottom;
 } RECT, *PRECT, *NPRECT, *LPRECT;

使用结构体时,调用代码如下:

using System;
using System.Runtime.InteropServices;
 namespace ConsoleApp10
 {
     public struct RECT
     {
         public int Left;
         public int Top;
         public int Right;
         public int Bottom;
     }
 
     class Program
     {
         [DllImport("user32.dll")]
         private static extern IntPtr GetDesktopWindow();
 
         [DllImport("user32.dll", SetLastError = true)]
         private static extern int GetWindowRect(IntPtr hwnd, out RECT rc);
 
         static void Main(string[] args)
         {
             IntPtr hwnd = GetDesktopWindow();
             RECT rect;
             GetWindowRect(hwnd, out rect);
             Console.WriteLine($"Left:{rect.Left},Top:{rect.Top},Right:{rect.Right},Bottom:{rect.Bottom}");
         }
     }
 }

运行结果如下:

下面我们来看一下,把结构体换成类的情况

 using System;
 using System.Runtime.InteropServices;
 
 namespace ConsoleApp10
 {
     public class RECT
     {
         public int Left;
         public int Top;
         public int Right;
         public int Bottom;
    }
 
     class Program
     {
         [DllImport("user32.dll")]
         private static extern IntPtr GetDesktopWindow();
 
         [DllImport("user32.dll", SetLastError = true)]
         private static extern int GetWindowRect(IntPtr hwnd,RECT rc);
 
         static void Main(string[] args)
         {
             IntPtr hwnd = GetDesktopWindow();
             RECT rect = new RECT();
             GetWindowRect(hwnd, rect);
             Console.WriteLine($"Left:{rect.Left},Top:{rect.Top},Right:{rect.Right},Bottom:{rect.Bottom}");
         }
     }
 }

运行结果如下:

运行结果并不正常

把类修改一下,带上[StructLayout(LayoutKind.Sequential)]

     [StructLayout(LayoutKind.Sequential)]
     public class RECT
     {
         public int Left;
         public int Top;
         public int Right;
         public int Bottom;
     }

再次运行,发现结果正常了

最后再看看LayoutKind.Explicit的情况,调用代码如下

 using System;
 using System.Runtime.InteropServices;
 
 namespace ConsoleApp10
 {
     [StructLayout(LayoutKind.Explicit)]
     public class RECT
     {
         [FieldOffset(0)]public int Left; //FieldOffset用于指示类或结构的非托管表示形式中字段的物理位置
         [FieldOffset(4)]public int Top;
         [FieldOffset(8)]public int Right;
         [FieldOffset(12)]public int Bottom;
     }
 
     class Program
     {
         [DllImport("user32.dll")]
         private static extern IntPtr GetDesktopWindow();
 
         [DllImport("user32.dll", SetLastError = true)]
         private static extern int GetWindowRect(IntPtr hwnd,[MarshalAs(UnmanagedType.LPStruct)]RECT rc);
 
         static void Main(string[] args)
         {
             IntPtr hwnd = GetDesktopWindow();
             RECT rect = new RECT();
             GetWindowRect(hwnd, rect);
             Console.WriteLine($"Left:{rect.Left},Top:{rect.Top},Right:{rect.Right},Bottom:{rect.Bottom}");
         }
     }
 }

以上就是C#使用StructLayout特性来控制内存结构的操作代码的详细内容,更多关于C# StructLayout控制内存结构的资料请关注脚本之家其它相关文章!

相关文章

  • C#异步执行任务的方法

    C#异步执行任务的方法

    这篇文章主要介绍了C#异步执行任务的方法,以一个简单实例形式分析了C#异步执行的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-07-07
  • C#事件用法实例浅析

    C#事件用法实例浅析

    这篇文章主要介绍了C#事件用法,以实例形式分析了C#中事件的定义、触发及处理相关技巧,需要的朋友可以参考下
    2015-05-05
  • Unity 实现贴花效果的制作教程

    Unity 实现贴花效果的制作教程

    有些游戏中的战斗痕迹的效果会通过贴花来实现的,贴花的方式多种多样。而在Unity中,有一种给官方文档提供代码的解决方案。本文将这些代码的基础上做一个绘图的贴花效果,感兴趣的童鞋可以参考一下
    2021-11-11
  • C#实现拼手气红包算法

    C#实现拼手气红包算法

    这篇文章主要为大家详细介绍了C#实现拼手气红包算法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-09-09
  • C#开发微信门户及应用(2) 微信消息处理和应答

    C#开发微信门户及应用(2) 微信消息处理和应答

    文章主要为大家详细介绍了C#开发微信门户及应用第二篇,微信消息处理和应答,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • C# Path类---文件路径解读

    C# Path类---文件路径解读

    这篇文章主要介绍了C# Path类---文件路径,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • C# MemoryStream类案例详解

    C# MemoryStream类案例详解

    这篇文章主要介绍了C# MemoryStream类案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • C#中XML基础用法

    C#中XML基础用法

    可扩展标记语言,标准通用标记语言的子集,简称XML。是一种用于标记电子文件使其具有结构性的标记语言。这篇文章介绍了C#中XML基础的用法,下面的实例代码,大家可以看看
    2021-12-12
  • 详解C#如何计算一个实例占用多少内存

    详解C#如何计算一个实例占用多少内存

    我们都知道CPU和内存是程序最为重要的两类指标,那么有多少人真正想过一个类型的实例在内存中究竟占多少字节,本文就来用C#计算一下一个实例占用多少内存吧
    2023-06-06
  • C#中图片如何自适应pictureBox大小

    C#中图片如何自适应pictureBox大小

    文章描述了如何在C#中实现图片自适应pictureBox大小,并展示修改前后的效果,修改步骤包括两步,作者分享了个人经验,希望对大家有所帮助
    2025-01-01

最新评论