我正在为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函数调用,必须以完全相同的方式完成此操作。仅函数名称和参数会更改,但三行调用块相同。这种形式的复制/粘贴三行函数调用看起来很便宜。
在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));
重读您的问题,我回答了错误的问题。您真正想要的是一个带有int的CheckErrorCode函数,然后只需传递本机调用的结果即可。
/// <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创建所谓的闭包。这是一种使调用ClibDllFunc3(特定于此函数)的逻辑脱离处理其结果的逻辑(这是所有DLL函数的标准配置)的一种方法。与许多闭包不同,此闭包立即被调用。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句