Соглашения о совместимости с C/С++ с С#
Рассмотрим следующую структуру API Win32:
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
При переносе этого объекта на С#, следует ли следовать правилам именования имен, которые используются здесь:
public struct _SECURITY_ATTRIBUTES
{
public int nLength;
public unsafe byte* lpSecurityDescriptor;
public int bInheritHandle;
}
или я могу изо всех сил и написать свою структуру в стиле С#, например:
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/С++.
Ответы
Ответ 1
Я думаю, это не должно быть личным предпочтением. Если вы переносите код на другой язык, вы должны применять соглашения на языке, на который вы портируете.
Я бы, конечно, использовал соглашения С#, так как код будет использоваться разработчиками С#, а не разработчиками C/С++, правильно? В противном случае мы все равно будем использовать уродливый _int32 DWORD
код в С#.
Вот несколько хороших руководств для конвенций С#:
Ответ 2
Это сводится к вашим личным предпочтениям. Ваше основное соображение, в котором подход к выбору должен быть простота обслуживания кода. Лично я предпочитаю придерживаться тех же имен, что и те, которые используются в декларации 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.
Конечно, я мог бы добавить красиво названные геттеры и сеттеры в структуре, например, public short Family...
Однако я пытаюсь скрыть структуры и методы взаимодействия из более простого в использовании интерфейса. Выражение определений интерполя низкого уровня просто не представляется необходимым.
Ответ 3
Я добавляю этот ответ ради полноты. Он объединяет точки из ответов Mert и dgvid. Вот пример использования структуры REST Win32.
Определение C/С++:
typedef struct _RECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT;
Определение С#:
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
}
}
Я чувствую, что это обеспечит несколько полное решение и будет соответствующим образом отображаться при использовании с собственными вызовами кода.
Дополнительная работа:
В настоящее время эта структура позволяет использовать Size, Point из пространства имен System.Drawing. Поскольку Win32 также имеет структуры размера и точки, будет также целесообразно также использовать их.