C#9 引入了非托管函数指针(例如delegate* unmanaged[Cdecl]<void>)。我一直在尝试这些以了解它们是如何工作的。升级到.NET 5.0.201后,我收到一个新警告:
error CS8909: Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct.
Run Code Online (Sandbox Code Playgroud)
根据这个问题,多次引用托管函数可能并不总是产生相同的指针。
以下是可能触发此警告的代码类型示例:
// saves a function pointer in unmanaged code
[DllImport("mylib", CallingConvention = CallingConvention.Cdecl)]
static extern void set_func(delegate* unmanaged[Cdecl]<void> func);
// retrieves the function pointer that was saved above
[DllImport("mylib", CallingConvention = CallingConvention.Cdecl)]
static extern delegate* unmanaged[Cdecl]<void> get_func();
// unmanaged function implemented in C#
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
static void MyFunc() {
...
}
void Test() {
// pass our unmanaged callers only function to unmanaged code
set_func(&MyFunc);
...
// sometime later we want to check if unmanaged code still has the same function
// pointer or if it changed
if (get_func() == (delegate* unmanaged[Cdecl]<void>)&MyFunc) { // this line triggers CS8909 warning
...
}
}
Run Code Online (Sandbox Code Playgroud)
&MyFunc我可以看到,如果第一次调用产生的函数指针与第二次调用 产生的函数指针不同,代码将无法正常工作&MyFunc。所以这个警告是有道理的。
可以使用什么替代方法来执行不会导致 CS8909 警告指示的问题的同类测试?
例如,这安全吗?
// identical code from above is omitted for brevity, only Test() method is changed
static readonly delegate* unmanaged[Cdecl]<void> myFunc = &MyFunc;
void Test() {
// pass our unmanaged callers only function to unmanaged code
set_func(myFunc);
...
// sometime later we want to check if unmanaged code still has the same function
// pointer or if it changed
#pragma warning disable CS8909
if (get_func() == myFunc) { // would still trigger warning CS8909 if it was enabled
#pragma warning restore CS8909
...
}
Run Code Online (Sandbox Code Playgroud)
是否有其他替代方案可以避免禁用警告?
将其分为三个部分。
set_func并存储从 1 开始的结果。get_func根据 1 存储的结果检查 的结果。这避免了这个问题,因为委托的转换不是双射,但比较它是完全有效的IntPtr。
一个例子:
void Test()
{
// create a GC root to the delegate
var myFuncDelegate = new Action(myFunc);
var pMyFunc = Marshal.GetFunctionPointerForDelegate(myFuncDelegate);
set_func(pMyFunc);
// ...
if (get_func() == pMyFunc)
{
// ...
}
// Ensure we don't collect myFuncDelegate while unmanaged code has a reference
GC.KeepAlive(myFuncDelegate);
}
Run Code Online (Sandbox Code Playgroud)