简体   繁体   中英

C/C++ Interoperability Naming Conventions with C#

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++.

This comes down to your personal preference. Your primary consideration in which approach to chose should be ease of code maintenance. Personally, I favor sticking with the exact same names as those used in the C declaration, because it makes it easier to understand what I'm looking at. For example, if I define a struct like so:

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

Then later I or a co-worker want to know just what the heck sa_family is, we can just search for documentation on sa_family. If I instead had a Family property, I would have the extra step of figuring out that it maps to sa_family.

Of course, I could put nicely named getters and setters on the struct, eg, public short Family... However, I try to hide interop structs and methods behind an easier-to-use interface. Spiffing up the low-level interop definitions just doesn't seem necessary.

I think this should not be a personal preference. If you are porting a code to another language, you should apply the conventions of the language that you are porting to.

I would certainly use C# conventions since the code will be used by C# developers, not C/C++ developers, right? Otherwise we would be still using ugly _int32 DWORD like code in C#.

Here are some good guides for C# conventions:

I am adding this answer for the sake of completeness. It combines points from Mert's and dgvid's answers. Here is an example using the Win32 RECT structure.

C/C++ definition:

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

C# definition:

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

I feel that this will provide a somewhat complete solution, and should map appropriately when used with native code calls.

Additional work:

This struct currently allows Size, Point to be used from the System.Drawing namespace. Since Win32 also has Size and Point structures, it will be appropriate to allow use of these too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM