简化c#中类似C宏的函数调用

本文关键字:函数调用 简化 | 更新日期: 2023-09-27 18:11:55

我正在为C dll编写包装器。为c#应用程序包装了各种C函数。现在考虑下面包装器的一些简化部分。

public enum ErrorCode
{
    OK = 0,
    ...
    ...
}
public class AppException: ApplicationException
{
    public AppException(ErrorCode errorCode) : base()
    {
        error = errorCode;
    }
    public ErrorCode error { get; private set; }
}
public class A
{
    public ErrorCode last_ret;
    private IntPtr datap;
    public A(string filename)
    {
        last_ret = (ErrorCode)ClibDllFunc1(filename, out datap);
        if (last_ret != ErrorCode.OK)
            throw new AppException(last_ret);
        // go on processing
        last_ret = (ErrorCode)ClibDllFunc2(datap);
        if (last_ret != ErrorCode.OK)
            throw new AppException(last_ret);
    }
    public void getSize(out int sz)
    {
        last_ret = (ErrorCode)ClibDllFunc3(datap, out sz);
        if (last_ret != ErrorCode.OK)
            throw new AppException(last_ret);
    }
    // ...
    // many functions like these, all working by calling c/c++ dll functions
    // with different number and types of parameters
}
[DllImport("clibrary.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
static extern internal int ClibDllFunc1(string filename, out IntPtr data);
// ... other C function declarations follow similarly
如您所见,包装器调用各种C函数。所有C函数都返回整数作为状态码(ErrorCode),如果C函数失败,包装器必须检查此返回码并抛出应用程序定义的异常。对于所有C函数调用,这必须以完全相同的方式完成。只有函数名和参数改变,但3行调用块是相同的。在这种形式下,复制/粘贴3行函数调用块看起来真的很便宜。 在c#中,是否有一种方法可以简化和封装"调用,检查返回代码,抛出异常"的循环,以一种更简单、更紧凑的方式?

作为参考(实际上这是我想做的,以简化调用);在C/c++中,我们可以像这样定义一个宏:

#define SAFE_CALL(call) do{ if((last_ret = (ErrorCode)call) != OK) throw AppException(last_ret); }while(0)

和这样调用:

SAFE_CALL(ClibDllFunc1(filename, &datap));
SAFE_CALL(ClibDllFunc2(datap));
SAFE_CALL(ClibDllFunc3(datap, &sz));

简化c#中类似C宏的函数调用

编辑

重读你的问题,我回答了错误的问题。你真正需要的是一个函数CheckErrorCode,它接受一个int,然后简单地传递本机调用的结果。
/// <summary>
/// Takes the result code from invoking a native function.  If the result is
/// not ErrorCode.OK, throws an AppException with that error code.
/// </summary>
/// <param name="returnCodeInt">
/// The return code of a native method call, as an integer. 
/// Will be cast to ErrorCode.
/// </param>
private static void CheckErrorCode(int returnCodeInt)
{
    ErrorCode returnCode = (ErrorCode)returnCodeInt;
    if(returnCode != ErrorCode.OK)
    {
        throw new AppException(returnCode);
    }
}
public void getSize(out int sz)
{
    CheckErrorCode(ClibDllFunc3(datap, out sz));
}

原始文本(如何使用lambdas模拟宏)

c#中的lambda语法非常简洁,这意味着您可以像使用宏一样使用Func<T>。试试这样做:

/// <summary>
/// Calls a function's native implementation, then checks if the error code 
/// is not ErrorCode.Ok.  If it is, throws an AppException.
/// </summary>
/// <param name="nativeCall">
/// A function that returns the status code as an int.
/// </param>
private static void CheckErrorCode(Func<int> nativeCall)
{
    var returnCode = (ErrorCode)nativeCall();
    if(returnCode != ErrorCode.OK)
    {
        throw new AppException(returnCode);
    }
}

你可以这样调用它:

public void getSize(out int sz)
{
    // drawback: compiler can't check that sz is always written.
    sz = 0;
    CheckErrorCode(() => ClibDllFunc3(datap, out sz));
}

lambda创建了一个闭包。这是一种将调用 ClibDllFunc3(特定于此函数)的逻辑与处理其结果的逻辑分离的方法(这是所有DLL函数的标准)。与许多闭包不同,这个闭包是立即调用的。