【问题标题】:C/C++ Interoperability Naming Conventions with C#C/C++ 与 C# 的互操作性命名约定
【发布时间】:2012-12-07 02:22:34
【问题描述】:

考虑以下 Win32 API 结构:

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

当将此对象移植到 C# 时,我是否应该遵循此处使用的名称命名约定,如下所示:

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

或者我可以全力以赴,用 C# 风格编写我的结构,如下所示:

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;
    }
}

虽然第二种方法看起来更优雅,但我想知道是否建议使用以这种方式编写的结构调用本机功能,或者从 C/C++ 移植结构时是否存在任何其他缺陷。

【问题讨论】:

  • 这取决于你。使用您喜欢的任何一个。但我不知道你为什么要在这里使用 unsafe 。我不会。
  • @DavidHeffernan,PInvoke.net 将此列为不安全。为什么不建议使用它?
  • 我认为没有令人信服的理由在这里使用不安全的代码。我会把它命名为IntPtr。就像 pinvoke.net 的其他版本一样。另外,不要把 pinvoke.net 当作福音。那里的很多代码都很糟糕。
  • @DavidHeffernan,好吧。我真的只是把它作为参考。谢谢,您的 cmets 很有帮助。

标签: c# c++ c winapi pinvoke


【解决方案1】:

这取决于您的个人喜好。您选择哪种方法的主要考虑因素应该是易于代码维护。就个人而言,我倾向于坚持使用与 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... 但是,我尝试将互操作结构和方法隐藏在更易于使用的接口后面。似乎没有必要扩展低级互操作定义。

【讨论】:

  • 您好,感谢您的回答。我实际上更倾向于使用 Mert 的答案,但这并不是说我已经驳回了你的任何答案。我认为给定示例,我可以为结构提供一个名为 Family 的属性,但在文档中,引用 sa_family 以便最终用户仍然知道要搜索什么。
【解决方案2】:

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

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

这里有一些很好的 C# 约定指南:

【讨论】:

  • 您好,感谢您的回答。我觉得这当然是最好的方法,尽管关于 dgvid 给出的答案,我认为参考文档中的原始代码很重要,这样最终用户才能确定 C# 对象与非托管对象的关系,本机对象。
【解决方案3】:

为了完整起见,我添加了这个答案。它结合了 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 结构,因此也允许使用它们是合适的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-21
    • 2021-05-19
    • 2011-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多