我正在为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));
答案 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));
}
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函数的标准)的逻辑中拉出来的方法。与许多闭包不同,这个闭包是立即调用的。