12

Consider the following Win32 API struct:

typedef struct _SECURITY_ATTRIBUTES {
  DWORD  nLength;
  LPVOID lpSecurityDescriptor;
  BOOL   bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;

When porting this object to C#, should I follow the name naming conventions used here, like so:

public struct _SECURITY_ATTRIBUTES
{
    public int nLength;
    public unsafe byte* lpSecurityDescriptor;
    public int bInheritHandle;
}

or can I go all out, and write my struct in a C# style, like so:

public struct SecurityAttributes
{
    private int length;
    private unsafe byte* securityDescriptor;
    private int bInheritHandle;

    public Int32 Length
    {
        get { return this.length; }
        set { this.length = value; }
    }

    public Byte* SecurityDescriptor
    {
        get { return this.seurityDescriptor; }
        set { this.securityDescriptor = value; }
    }

    public Int32 InheritHandle
    {
        get { return this.bInheritHandle; }
        set { this.bInheritHandle = value; }
    }

    public SecurityAttributes(int length, byte* securityDescriptor, int inheritHandle)
    {
        this.length = length;
        this.securityDescriptor = securityDescriptor;
        this.inheritHandle = inheritHandle;
    }
}

Whilst the second approach seems much more elegant, I would like to know if it is advisable to invoke native functionality using a struct written in that fashion, or if there are any other pitfalls when porting structs from C/C++.

4

3 回答 3

4

这取决于您的个人喜好。您选择哪种方法的主要考虑因素应该是易于代码维护。就我个人而言,我倾向于坚持使用与 C 声明中使用的名称完全相同的名称,因为这样更容易理解我在看什么。例如,如果我这样定义一个结构:

/// <summary>
/// Unmanaged sockaddr_in structure from Ws2def.h.
/// </summary>
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct sockaddr_in
{
    /// <summary>
    /// short sa_family;
    /// </summary>
    public short sa_family;

    /// <summary>
    /// unsigned short sin_port;
    /// </summary>
    public ushort sin_port;

    /// <summary>
    /// struct in_addr addr;
    /// </summary>
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 4)]
    public byte[] addr;

    /// <summary>
    /// char sin_zero[8];
    /// </summary>
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 8)]
    public byte[] sin_zero;
}

稍后我或同事想知道到底sa_family是什么,我们可以在 sa_family 上搜索文档。如果我有一个 Family 属性,我将有一个额外的步骤来确定它映射到 sa_family。

当然,我可以在结构上放置命名良好的 getter 和 setter,例如,public short Family...但是,我尝试将互操作结构和方法隐藏在更易于使用的接口后面。似乎没有必要增加低级互操作定义。

于 2012-12-19T19:12:16.320 回答
3

我认为这不应该是个人喜好。如果要将代码移植到另一种语言,则应应用要移植到的语言的约定。

我当然会使用 C# 约定,因为代码将由 C# 开发人员使用,而不是 C/C++ 开发人员,对吧?否则我们仍然会_int32 DWORD在 C# 中使用丑陋的代码。

以下是 C# 约定的一些很好的指南:

于 2012-12-19T19:28:52.903 回答
0

为了完整起见,我添加了这个答案。它结合了 Mert 和 dgvid 答案的要点。这是一个使用 Win32 RECT 结构的示例。

C/C++ 定义:

typedef struct _RECT {
  LONG left;
  LONG top;
  LONG right;
  LONG bottom;
} RECT, *PRECT;

C#定义:

namespace NetBlast.Runtime.PlatformInvoke.Windows
{
    #region USING

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Text;

    #endregion

    /// <summary>
    /// The Rect (RECT) structure defines the coordinates of the upper-left and lower-right corners of a rectangle.
    /// </summary>
    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct Rect : IEquatable<Rect>, IEquatable<Rectangle>, ICloneable
    {
        #region CONSTANTS



        #endregion

        #region VARIABLES

        /// <summary>
        /// Win32 RECT.left value.
        /// </summary>
        private int left;

        /// <summary>
        /// Win32 RECT.top value.
        /// </summary>
        private int top;

        /// <summary>
        /// Win32 RECT.right value.
        /// </summary>
        private int right;

        /// <summary>
        /// Win32 RECT.bottom value.
        /// </summary>
        private int bottom;

        #endregion

        #region PROPERTIES

        /// <summary>
        /// Gets or sets the x-coordinate of the upper-left corner of the rectangle.
        /// </summary>
        public Int32 Left
        {
            get { return this.left; }
            set { this.left = value; }
        }

        /// <summary>
        /// Gets or sets the y-coordinate of the upper-left corner of the rectangle.
        /// </summary>
        public Int32 Top
        {
            get { return this.top; }
            set { this.top = value; }
        }

        /// <summary>
        /// Gets or sets the x-coordinate of the lower-right corner of the rectangle.
        /// </summary>
        public Int32 Right
        {
            get { return this.right; }
            set { this.right = value; }
        }

        /// <summary>
        /// Gets or sets the y-coordinate of the lower-right corner of the rectangle.
        /// </summary>
        public Int32 Bottom
        {
            get { return this.bottom; }
            set { this.bottom = value; }
        }

        /// <summary>
        /// Gets or sets the height of the rectangle.
        /// </summary>
        public Int32 Height
        {
            get { return this.bottom - this.top; }
            set { this.bottom = value + this.top; }
        }

        /// <summary>
        /// Gets or sets the width of the rectangle.
        /// </summary>
        public Int32 Width
        {
            get { return this.right - this.left; }
            set { this.right = value + this.left; }
        }

        /// <summary>
        /// Gets or sets the top, left location of the rectangle.
        /// </summary>
        public Point Location
        {
            get
            {
                return new Point(this.left, this.top);
            }

            set
            {
                this.right = this.left - value.X;
                this.bottom = this.top - value.Y;
                this.left = value.X;
                this.top = value.Y;
            }
        }

        /// <summary>
        /// Gets or sets the size of the rectangle.
        /// </summary>
        public Size Size
        {
            get
            {
                return new Size(this.Width, this.Height);
            }

            set
            {
                this.right = value.Width + this.left;
                this.bottom = value.Height + this.top;
            }
        }

        #endregion

        #region CONSTRUCTORS / FINALIZERS

        /// <summary>
        /// Initializes a new instance of the <see cref="Rect" /> struct.
        /// </summary>
        /// <param name="location">The top, left location of the rectangle.</param>
        /// <param name="size">The size of the rectangle.</param>
        public Rect(Point location, Size size)
        {
            this.left = default(int);
            this.top = default(int);
            this.right = default(int);
            this.bottom = default(int);
            this.Location = location;
            this.Size = size;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Rect" /> struct.
        /// </summary>
        /// <param name="left">The x-coordinate of the upper-left corner of the rectangle.</param>
        /// <param name="top">The y-coordinate of the upper-left corner of the rectangle.</param>
        /// <param name="right">The x-coordinate of the lower-right corner of the rectangle.</param>
        /// <param name="bottom">The y-coordinate of the lower-right corner of the rectangle.</param>
        public Rect(int left, int top, int right, int bottom)
        {
            this.left = left;
            this.top = top;
            this.right = right;
            this.bottom = bottom;
        }

        #endregion

        #region OPERATORS

        /// <summary>
        /// Provides implicit casting from Rect to Rectangle.
        /// </summary>
        /// <param name="rectangle">The Rect instance to cast.</param>
        /// <returns>A Rectangle representation of the Rect instance.</returns>
        public static implicit operator Rectangle(Rect rectangle)
        {
            return new Rectangle(rectangle.Location, rectangle.Size);
        }

        /// <summary>
        /// Provides implicit casting from Rectangle to Rect.
        /// </summary>
        /// <param name="rectangle">The Rectangle instance to cast.</param>
        /// <returns>A Rect representation of the Rectangle instance.</returns>
        public static implicit operator Rect(Rectangle rectangle)
        {
            return new Rect(rectangle.Location, rectangle.Size);
        }

        /// <summary>
        /// Performs an equality check between two instances of Rect.
        /// </summary>
        /// <param name="a">Instance a of Rect.</param>
        /// <param name="b">Instance b of Rect.</param>
        /// <returns>True if the instances are equal, otherwise false.</returns>
        public static bool operator ==(Rect a, Rect b)
        {
            return a.Equals(b);
        }

        /// <summary>
        /// Performs an inequality check between two instances of Rect.
        /// </summary>
        /// <param name="a">Instance a of Rect.</param>
        /// <param name="b">Instance b of Rect.</param>
        /// <returns>True if the instances are not equal, otherwise false.</returns>
        public static bool operator !=(Rect a, Rect b)
        {
            return !a.Equals(b);
        }

        #endregion

        #region STATIC METHODS



        #endregion

        #region INSTANCE METHODS

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="obj">An object to compare with this object.</param>
        /// <returns>True if the instances are not equal, otherwise false.</returns>
        public override bool Equals(object obj)
        {
            return this.Equals((Rect)obj);
        }

        /// <summary>
        /// Serves as a hash function for this instance of Rect.
        /// </summary>
        /// <returns>A hash code for the current Rect.</returns>
        public override int GetHashCode()
        {
            return ObjectUtilities.CreateHashCode(this.left, this.top, this.right, this.bottom);
        }

        /// <summary>
        /// Returns a string representation of this instance.
        /// </summary>
        /// <returns>A string representation of this instance.</returns>
        public override string ToString()
        {
            return string.Format("Left: {0}; Top: {1}; Right: {2}; Bottom: {3};", this.left, this.top, this.right, this.bottom);
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">A Rect instance to compare with this object.</param>
        /// <returns>True if the instances are not equal, otherwise false.</returns>
        public bool Equals(Rect other)
        {
            return this.left == other.left
                && this.top == other.top
                && this.right == other.right
                && this.bottom == other.bottom;
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">A Rectangle instance to compare with this object.</param>
        /// <returns>True if the instances are not equal, otherwise false.</returns>
        public bool Equals(Rectangle other)
        {
            return this.left == other.Left
                && this.top == other.Top
                && this.right == other.Right
                && this.bottom == other.Bottom;
        }

        /// <summary>
        /// Returns a clone of this Rect instance.
        /// </summary>
        /// <returns>A clone of this Rect instance.</returns>
        public object Clone()
        {
            return new Rect(this.left, this.top, this.right, this.bottom);
        }

        #endregion

        #region DELEGATES & EVENTS



        #endregion

        #region CLASSES & STRUCTURES



        #endregion
    }
}

我觉得这将提供一个有点完整的解决方案,并且在与本机代码调用一起使用时应该适当地映射。

附加工作:

此结构当前允许从 System.Drawing 命名空间使用 Size、Point。由于 Win32 也有 Size 和 Point 结构,因此也允许使用它们是合适的。

于 2012-12-21T10:07:42.187 回答