我应该如何将这个 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;
    }
}