简化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));
编辑
重读你的问题,我回答了错误的问题。你真正需要的是一个函数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函数的标准)。与许多闭包不同,这个闭包是立即调用的。