C#Interop:输出参数,也可以为null

C#Interop:输出参数,也可以为null,c#,interop,C#,Interop,考虑以下DllImport: [DllImport("lulz.so")] public static extern int DoSomething(IntPtr SomeParam); 这实际上是引用一个C风格的函数,如下所示: int DoSomething(void* SomeParam); 假设SomeParam是一个“out”参数,但也可以为NULL。如果参数为NULL,则C函数的行为不同。所以我可能想: [DllImport("lulz.so")] public static

考虑以下DllImport:

[DllImport("lulz.so")]
public static extern int DoSomething(IntPtr SomeParam);
这实际上是引用一个C风格的函数,如下所示:

int DoSomething(void* SomeParam); 
假设SomeParam是一个“out”参数,但也可以为NULL。如果参数为NULL,则C函数的行为不同。所以我可能想:

[DllImport("lulz.so")]
public static extern int DoSomething(out IntPtr SomeParam);
但是,如果我在导入中将其设置为out参数,则无法将其传递为NULL,即,我无法执行以下操作:

int retVal = DoSomething(IntPtr.Zero)

我的选择是什么

如果您试图传递一个值,那么
out
不是正确的关键字;将其更改为
ref
。您仍然需要显式地传递变量,但它可以是
null
引用

例如

[DllImport("lulz.so")]
public static extern int DoSomething(ref IntPtr SomeParam);
你可以这样称呼它:

IntPtr retVal = IntPtr.Zero;

DoSomething(ref retVal);
public void DoSomething()
{
    IntPtr dummy;
    DoSomething(out dummy);
}
然而 是什么告诉您它需要是
out
ref
?将
IntPtr
作为
out
ref
传递实际上类似于传递双指针。实际上,将参数作为
IntPtr
传递似乎更合适


典型的过程是在托管代码中分配必要的内存并传递表示该分配内存的
IntPtr
,或者
IntPtr.Zero
表示空指针。您无需将
IntPtr
作为
out
ref
传递,即可将数据发送回.NET;如果您正在调用的函数实际上会更改指针的地址,则只需执行此操作。

如果您试图传递值,则
out
不是正确的关键字;将其更改为
ref
。您仍然需要显式地传递变量,但它可以是
null
引用

例如

[DllImport("lulz.so")]
public static extern int DoSomething(ref IntPtr SomeParam);
你可以这样称呼它:

IntPtr retVal = IntPtr.Zero;

DoSomething(ref retVal);
public void DoSomething()
{
    IntPtr dummy;
    DoSomething(out dummy);
}
然而 是什么告诉您它需要是
out
ref
?将
IntPtr
作为
out
ref
传递实际上类似于传递双指针。实际上,将参数作为
IntPtr
传递似乎更合适


典型的过程是在托管代码中分配必要的内存并传递表示该分配内存的
IntPtr
,或者
IntPtr.Zero
表示空指针。您无需将
IntPtr
作为
out
ref
传递,即可将数据发送回.NET;如果您正在调用的函数实际上会更改指针的地址,您只需要这样做。

我不明白问题出在哪里

这是:

private void button2_Click(object sender, EventArgs e) {
    object bar;
    Method(out bar);

    bar = IntPtr.Zero;
    Method(out bar);
}

private void Method(out object foo) {
    foo = null;
}

我不明白问题是什么

这是:

private void button2_Click(object sender, EventArgs e) {
    object bar;
    Method(out bar);

    bar = IntPtr.Zero;
    Method(out bar);
}

private void Method(out object foo) {
    foo = null;
}

传递
NULL
的意图是什么?是否打算像往常一样调用该方法,而只是不设置输出参数

在这种情况下,我想我应该用C#中的重载来包装extern方法。过载(不带
out
参数)如下所示:

IntPtr retVal = IntPtr.Zero;

DoSomething(ref retVal);
public void DoSomething()
{
    IntPtr dummy;
    DoSomething(out dummy);
}

传递
NULL
的意图是什么?是否打算像往常一样调用该方法,而只是不设置输出参数

在这种情况下,我想我应该用C#中的重载来包装extern方法。过载(不带
out
参数)如下所示:

IntPtr retVal = IntPtr.Zero;

DoSomething(ref retVal);
public void DoSomething()
{
    IntPtr dummy;
    DoSomething(out dummy);
}

我曾经碰到过这个。最后我自己封送了指针(请参阅库函数的相关内容)。

我曾经遇到过这个问题。最后我自己封送了指针(请参阅库函数的相关内容)。

我个人会导入此函数两次,第一次使用'out'参数,第二次使用'in'

[DllImport("lulz.so")]
public static extern int DoSomething(out IntPtr SomeParam);

// call as DoSomethingWithNull(IntPtr.Zero)
[DllImport("lulz.so", EntryPoint="DoSomething")]
public static extern int DoSomethingWithNull(IntPtr SomeParam);

这将解决您的问题,并使代码更具可读性。

就我个人而言,我将导入此函数两次,第一次使用'out'参数,第二次使用'in'

[DllImport("lulz.so")]
public static extern int DoSomething(out IntPtr SomeParam);

// call as DoSomethingWithNull(IntPtr.Zero)
[DllImport("lulz.so", EntryPoint="DoSomething")]
public static extern int DoSomethingWithNull(IntPtr SomeParam);

这将解决您的问题,并使代码更具可读性。

正确。如果输入变量为NULL,则函数只会执行一些不同的操作,而不是保存一些分配的内存进行填充。如果它确实执行一些不同的操作,则不要使用我的示例。我的示例假设唯一的区别是是否填充了out参数,因此我觉得重载是可以的。我想我还可以提供相同函数的两个dllimport,一个参数标记为out,另一个没有?未标记为out的将接受NULL?尽管这样做似乎很粗略。
ref
out
是同一件事,它们只是在函数本身内部提供了不同的语义:即在使用函数之前必须在函数内部设置
out
参数,并且必须在函数返回之前设置参数。就我所知,两者的底层代码是相同的。@Matthew:就我所知,对于标准的.NET调用,您是正确的。但是,在讨论互操作封送时,关键字的差异确实会产生影响。正确。如果输入变量为NULL,则函数只会执行一些不同的操作,而不是保存一些分配的内存进行填充。如果它确实执行一些不同的操作,则不要使用我的示例。我的示例假设唯一的区别是是否填充了out参数,因此我觉得重载是可以的。我想我还可以提供相同函数的两个dllimport,一个参数标记为out,另一个没有?未标记为out的将接受NULL?尽管这样做似乎很粗略。
ref
out
是同一件事,它们只是在函数本身内部提供了不同的语义:即在使用函数之前必须在函数内部设置
out
参数,并且必须在函数返回之前设置参数。就我所知,两者的底层代码是相同的。@Matthew:就我所知,对于标准的.NET调用,您是正确的。但是,在谈到互操作封送处理时,关键字的差异确实会产生影响。不过,在互操作设置中是否真的要传递null呢?您不是要传递bar的地址吗?out参数永远不会真正传入。我不知道该怎么办