C#中简化的C-macro-like函数调用

时间:2014-07-07 04:27:32

标签: c# macros

我正在为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));

1 个答案:

答案 0 :(得分:2)

修改

重读你的问题,我回答了错误的问题。你真正想要的是一个函数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创建了所谓的closure。这是一种将调用 ClibDllFunc3(特定于此函数)的逻辑从处理其结果(这是所有DLL函数的标准)的逻辑中拉出来的方法。与许多闭包不同,这个闭包是立即调用的。