我应该如何将这个 C 函数指针结构移植到 C# .NET 实现
How should I port this struct of C function pointers to a C# .NET implementation
我正在努力将通常用 C/C++ 为 WIN32 编写的引导加载程序主机库移植到 C# Class 库(而不是必须使用与原始库的互操作,希望能使其更易于使用和调试)。作为一个不太熟悉 OOP 的人,我正在尝试弄清楚如何将库的这一特定功能实现到 C# .NET(VS2019、.NET Standard 2.0)
在原来的C库中,我们在库源代码的头文件中有如下结构:
typedef struct
{
/* Function used to open the communications connection */
int (*OpenConnection)(void);
/* Function used to close the communications connection */
int (*CloseConnection)(void);
/* Function used to read data over the communications connection */
int (*ReadData)(uint8_t*, int);
/* Function used to write data over the communications connection */
int (*WriteData)(uint8_t*, int);
/* Value used to specify the maximum number of bytes that can be transfered at a time */
unsigned int MaxTransferSize;
} CommunicationsData;
这是由内部库函数调用的,如下所示:
static CommunicationsData* g_comm;
int Bootloader_TransferData(uint8_t* inBuf, int inSize, uint8_t* outBuf, int outSize)
{
int err = g_comm->WriteData(inBuf, inSize);
if (CYRET_SUCCESS == err)
err = g_comm->ReadData(outBuf, outSize);
if (CYRET_SUCCESS != err)
err |= CYRET_ERR_COMM_MASK;
return err;
}
这个想法是库本身与通信协议无关,使用库的应用程序是您编写 OpenConnection()/CloseConnection()/ReadData()/WriteData() 的实现以及提供MaxTransferSize 的值。在使用带有互操作的原始 C 库的 C# 应用程序中,它是这样完成的:
class Bootloader_Utils{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int OpenConnection();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int CloseConnection();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int ReadData(IntPtr buffer, int size);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int WriteData(IntPtr buffer, int size);
/// <summary>
/// Structure used to pass communication data down to the unmanged native C code
/// that handles the bootloading operations.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct CommunicationsData
{
/// <summary>
/// Function used to open the communications connection
/// </summary>
public OpenConnection OpenConnection;
/// <summary>
/// Function used to close the communications connection
/// </summary>
public CloseConnection CloseConnection;
/// <summary>
/// Function used to read data over the communications connection
/// </summary>
public ReadData ReadData;
/// <summary>
/// Function used to write data over the communications connection
/// </summary>
public WriteData WriteData;
/// <summary>
/// Value used to specify the maximum number of bytes that can be transferred at a time
/// </summary>
public uint MaxTransferSize;
};
}
然后您将在应用程序代码中创建一个新的 CommunicationsData 对象并分配方法:
Bootload_Utils.CommunicationsData comm_data = new Bootload_Utils.CommunicationsData();
comm_data.OpenConnection = OpenConnection;
comm_data.CloseConnection = CloseConnection;
comm_data.ReadData = ReadData;
comm_data.WriteData = WriteData;
comm_data.MaxTransferSize = 64;
然后在 C# 应用程序中定义方法,例如 OpenConnection() 的示例:
public int OpenConnection()
{
int status = (int)ReturnCodes.CYRET_SUCCESS;
if (ConnectionStatus == false)
{
try
{
serialPort.Open();
ConnectionStatus = true;
}
catch (Exception exc)
{
ConnectionStatus = false;
SetText(tb_StatusLog, " Error in opening serial port: " + exc.Message + "\r\n");
serialPort.Close();
}
}
return status;
}
我应该如何在 C# 中复制此行为?我的想法是这样的:
public class CommunicationData
{
delegate int OpenConnection();
delegate int CloseConnection();
delegate int ReadData(ref byte[] dataBuf, int numBytes);
delegate int WriteData(ref byte[] dataBuf, int numBytes);
int MaxTransferSize { get; set; }
}
然后我可以在库中创建这个 class 的新实例并调用方法:
CommunicationData g_Comm = new CommunicationData();
int err = g_Comm.OpenConnection();
但这不太正确,因为它仍然需要 OpenConnection() 的定义,我希望它位于应用程序中,而不是库中。
我走的路对吗?如何在 .NET class 库中复制此功能?
如果应用程序在您编写时提供 OpenConnection
,使用 delegate
可能是正确的方法。然后你需要在应用程序中定义这些方法并填充结构。
或者,CommunicationData
可以成为您的应用程序或其他一些 class 需要实现的 interface
,并且您将传递 that 到库函数。或者可以将库包装在 class 中(g_comm
成为一个字段)并将接口仅传递给它的构造函数。
我同意@numzero 的观点,您可能正在寻找一个接口。但如果你想更接近原始实现:
public class CommunicationData
{
public Func<int> OpenConnection { get; set; }
//etc
int MaxTransferSize { get; set; }
}
这允许你做:
var data = new CommunicationData();
data.OpenConnection = OpenConnection;
data.OpenConnection();
使用界面
//in your lib
public interface ICommunicationData
{
int OpenConnection();
int CloseConnection();
int ReadData(byte[] dataBuf, int numBytes);
int WriteData(byte[] dataBuf, int numBytes);
int MaxTransferSize { get; set; }
}
//in your app
public class SerialCommunicationData : ICommunicationData
{
public int MaxTransferSize { get; set; }
public int CloseConnection()
{
return 1;
}
public int OpenConnection()
{
return 1;
}
public int ReadData(byte[] dataBuf, int numBytes)
{
return 1;
}
public int WriteData(byte[] dataBuf, int numBytes)
{
return 1;
}
}
public class UsbCommunicationData : ICommunicationData
{
public int MaxTransferSize { get; set; }
public int CloseConnection()
{
return 1;
}
public int OpenConnection()
{
return 1;
}
public int ReadData(byte[] dataBuf, int numBytes)
{
return 1;
}
public int WriteData(byte[] dataBuf, int numBytes)
{
return 1;
}
}
我正在努力将通常用 C/C++ 为 WIN32 编写的引导加载程序主机库移植到 C# Class 库(而不是必须使用与原始库的互操作,希望能使其更易于使用和调试)。作为一个不太熟悉 OOP 的人,我正在尝试弄清楚如何将库的这一特定功能实现到 C# .NET(VS2019、.NET Standard 2.0)
在原来的C库中,我们在库源代码的头文件中有如下结构:
typedef struct
{
/* Function used to open the communications connection */
int (*OpenConnection)(void);
/* Function used to close the communications connection */
int (*CloseConnection)(void);
/* Function used to read data over the communications connection */
int (*ReadData)(uint8_t*, int);
/* Function used to write data over the communications connection */
int (*WriteData)(uint8_t*, int);
/* Value used to specify the maximum number of bytes that can be transfered at a time */
unsigned int MaxTransferSize;
} CommunicationsData;
这是由内部库函数调用的,如下所示:
static CommunicationsData* g_comm;
int Bootloader_TransferData(uint8_t* inBuf, int inSize, uint8_t* outBuf, int outSize)
{
int err = g_comm->WriteData(inBuf, inSize);
if (CYRET_SUCCESS == err)
err = g_comm->ReadData(outBuf, outSize);
if (CYRET_SUCCESS != err)
err |= CYRET_ERR_COMM_MASK;
return err;
}
这个想法是库本身与通信协议无关,使用库的应用程序是您编写 OpenConnection()/CloseConnection()/ReadData()/WriteData() 的实现以及提供MaxTransferSize 的值。在使用带有互操作的原始 C 库的 C# 应用程序中,它是这样完成的:
class Bootloader_Utils{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int OpenConnection();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int CloseConnection();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int ReadData(IntPtr buffer, int size);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int WriteData(IntPtr buffer, int size);
/// <summary>
/// Structure used to pass communication data down to the unmanged native C code
/// that handles the bootloading operations.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct CommunicationsData
{
/// <summary>
/// Function used to open the communications connection
/// </summary>
public OpenConnection OpenConnection;
/// <summary>
/// Function used to close the communications connection
/// </summary>
public CloseConnection CloseConnection;
/// <summary>
/// Function used to read data over the communications connection
/// </summary>
public ReadData ReadData;
/// <summary>
/// Function used to write data over the communications connection
/// </summary>
public WriteData WriteData;
/// <summary>
/// Value used to specify the maximum number of bytes that can be transferred at a time
/// </summary>
public uint MaxTransferSize;
};
}
然后您将在应用程序代码中创建一个新的 CommunicationsData 对象并分配方法:
Bootload_Utils.CommunicationsData comm_data = new Bootload_Utils.CommunicationsData();
comm_data.OpenConnection = OpenConnection;
comm_data.CloseConnection = CloseConnection;
comm_data.ReadData = ReadData;
comm_data.WriteData = WriteData;
comm_data.MaxTransferSize = 64;
然后在 C# 应用程序中定义方法,例如 OpenConnection() 的示例:
public int OpenConnection()
{
int status = (int)ReturnCodes.CYRET_SUCCESS;
if (ConnectionStatus == false)
{
try
{
serialPort.Open();
ConnectionStatus = true;
}
catch (Exception exc)
{
ConnectionStatus = false;
SetText(tb_StatusLog, " Error in opening serial port: " + exc.Message + "\r\n");
serialPort.Close();
}
}
return status;
}
我应该如何在 C# 中复制此行为?我的想法是这样的:
public class CommunicationData
{
delegate int OpenConnection();
delegate int CloseConnection();
delegate int ReadData(ref byte[] dataBuf, int numBytes);
delegate int WriteData(ref byte[] dataBuf, int numBytes);
int MaxTransferSize { get; set; }
}
然后我可以在库中创建这个 class 的新实例并调用方法:
CommunicationData g_Comm = new CommunicationData();
int err = g_Comm.OpenConnection();
但这不太正确,因为它仍然需要 OpenConnection() 的定义,我希望它位于应用程序中,而不是库中。
我走的路对吗?如何在 .NET class 库中复制此功能?
如果应用程序在您编写时提供 OpenConnection
,使用 delegate
可能是正确的方法。然后你需要在应用程序中定义这些方法并填充结构。
或者,CommunicationData
可以成为您的应用程序或其他一些 class 需要实现的 interface
,并且您将传递 that 到库函数。或者可以将库包装在 class 中(g_comm
成为一个字段)并将接口仅传递给它的构造函数。
我同意@numzero 的观点,您可能正在寻找一个接口。但如果你想更接近原始实现:
public class CommunicationData
{
public Func<int> OpenConnection { get; set; }
//etc
int MaxTransferSize { get; set; }
}
这允许你做:
var data = new CommunicationData();
data.OpenConnection = OpenConnection;
data.OpenConnection();
使用界面
//in your lib
public interface ICommunicationData
{
int OpenConnection();
int CloseConnection();
int ReadData(byte[] dataBuf, int numBytes);
int WriteData(byte[] dataBuf, int numBytes);
int MaxTransferSize { get; set; }
}
//in your app
public class SerialCommunicationData : ICommunicationData
{
public int MaxTransferSize { get; set; }
public int CloseConnection()
{
return 1;
}
public int OpenConnection()
{
return 1;
}
public int ReadData(byte[] dataBuf, int numBytes)
{
return 1;
}
public int WriteData(byte[] dataBuf, int numBytes)
{
return 1;
}
}
public class UsbCommunicationData : ICommunicationData
{
public int MaxTransferSize { get; set; }
public int CloseConnection()
{
return 1;
}
public int OpenConnection()
{
return 1;
}
public int ReadData(byte[] dataBuf, int numBytes)
{
return 1;
}
public int WriteData(byte[] dataBuf, int numBytes)
{
return 1;
}
}