如何将可选指针参数从 C++ 代码转换为 C#
How to convert optional pointer argument from C++ code to C#
我在 C++ 中有这样的函数:
// C++
bool Foo(int* retVal = NULL)
{
// ...
if (retVal != NULL)
*retVal = 5;
// ...
return true;
}
我可以通过两种方式使用函数:
int ret;
Foo(&ret);
Foo();
当我用 C# 编写代码时,我使用了 ref 关键字:
// C#
bool Foo(ref int retVal = null)
{
// ...
if (retVal != null)
{
retVal = 5;
}
// ...
return true;
}
但是编译器说:
ref 或 out 参数不能有默认值。
我该如何解决这个问题?
C#的ref
更类似于C++的&
(引用)而不是C和C++的*
(指针)。作为 C++ 引用,他们必须引用一些东西。
现在,您可以:
public class OptionalRef<T>
{
public T Value { get; set; }
public static implicit operator OptionalRef<T>(T value)
{
return new OptionalRef<T> { Value = value };
}
public static implicit operator T(OptionalRef<T> optional)
{
return optional.Value;
}
public override string ToString()
{
return Value != null ? Value.ToString() : null;
}
}
然后
static bool Foo(OptionalRef<int> retVal = null)
{
// ...
if (retVal != null)
{
retVal.Value = 5;
}
// ...
return true;
}
你可以像这样使用它:
Foo(); // null passed
Foo(null); // same
Foo(5); // not interested if the value is changed
// Full use
OptionalRef<int> val = 5;
Foo(val);
int ret = val;
请注意,我并不完全认可我写的语义
更多的是你要东西,我给你东西,不问
最简单的方法就是写一个重载:
bool Foo()
{
int notUsed;
return Foo(ref notUsed);
}
bool Foo(ref int retVal)
{
// ...
retVal = 5;
// ...
return true;
}
如果您确实需要知道是否需要 ref
值,那么您仍然可以使用指针,但您需要一个 unsafe
上下文:
unsafe bool Foo()
{
return Foo(null);
}
unsafe bool Foo(ref int retVal)
{
return Foo(&retVal);
}
private unsafe bool Foo(int* retVal)
{
// ...
if (retVal != null)
{
*retVal = 5;
}
// ...
return true;
}
或者,没有评论中建议的unsafe
,因为C#中的指针可能被认为是重炮:
bool Foo()
{
var notNeeded = 0;
return Foo(ref notNeeded, false);
}
bool Foo(ref int retVal)
{
return Foo(ref retVal, true);
}
private bool Foo(ref int retVal, bool retValNeeded)
{
// ...
if (retValNeeded)
{
retVal = 5;
}
// ...
return true;
}
我会选择两种方法中的一种。如果您真的想要引用您的对象,您可以将其包装在 class 中。这些总是通过引用传递,因此您可以按照自己的方式进行修改,并且可以将其实例化为 null。
这是一个例子。
public class HolderObject
{
public string myStr {get; set;}
}
public class Program
{
public static void Main()
{
var xyz = new HolderObject() {
myStr = "1234"
};
Console.WriteLine(xyz.myStr);
FixString(xyz);
Console.WriteLine(xyz.myStr);
FixString();
Console.WriteLine(xyz.myStr);
}
private static bool FixString(HolderObject input = null)
{
if (input != null)
input.myStr = "test";
return true;
}
}
打印
1234
test
另一个解决方案是重载您的函数。
bool Foo()
{
// ...
return true;
}
bool Foo(ref int retVal = null)
{
// ...
if (retVal != null)
{
retVal = 5;
}
return Foo();
}
我真的不喜欢这个。实际上,我正在处理直接从 C++ 中提取的 C# 代码。嵌套 6 或 7 层的函数修改通过引用传递的对象。这很难阅读,如果您查看代码分析警告,它会建议您不要使用 ref 值。
如果可以的话,我会尽可能地避免传递 ref 并 return 返回已修改的值。或者传回一个包含您的布尔值和新值的对象。
我在 C++ 中有这样的函数:
// C++
bool Foo(int* retVal = NULL)
{
// ...
if (retVal != NULL)
*retVal = 5;
// ...
return true;
}
我可以通过两种方式使用函数:
int ret;
Foo(&ret);
Foo();
当我用 C# 编写代码时,我使用了 ref 关键字:
// C#
bool Foo(ref int retVal = null)
{
// ...
if (retVal != null)
{
retVal = 5;
}
// ...
return true;
}
但是编译器说:
ref 或 out 参数不能有默认值。
我该如何解决这个问题?
C#的ref
更类似于C++的&
(引用)而不是C和C++的*
(指针)。作为 C++ 引用,他们必须引用一些东西。
现在,您可以:
public class OptionalRef<T>
{
public T Value { get; set; }
public static implicit operator OptionalRef<T>(T value)
{
return new OptionalRef<T> { Value = value };
}
public static implicit operator T(OptionalRef<T> optional)
{
return optional.Value;
}
public override string ToString()
{
return Value != null ? Value.ToString() : null;
}
}
然后
static bool Foo(OptionalRef<int> retVal = null)
{
// ...
if (retVal != null)
{
retVal.Value = 5;
}
// ...
return true;
}
你可以像这样使用它:
Foo(); // null passed
Foo(null); // same
Foo(5); // not interested if the value is changed
// Full use
OptionalRef<int> val = 5;
Foo(val);
int ret = val;
请注意,我并不完全认可我写的语义
更多的是你要东西,我给你东西,不问
最简单的方法就是写一个重载:
bool Foo()
{
int notUsed;
return Foo(ref notUsed);
}
bool Foo(ref int retVal)
{
// ...
retVal = 5;
// ...
return true;
}
如果您确实需要知道是否需要 ref
值,那么您仍然可以使用指针,但您需要一个 unsafe
上下文:
unsafe bool Foo()
{
return Foo(null);
}
unsafe bool Foo(ref int retVal)
{
return Foo(&retVal);
}
private unsafe bool Foo(int* retVal)
{
// ...
if (retVal != null)
{
*retVal = 5;
}
// ...
return true;
}
或者,没有评论中建议的unsafe
,因为C#中的指针可能被认为是重炮:
bool Foo()
{
var notNeeded = 0;
return Foo(ref notNeeded, false);
}
bool Foo(ref int retVal)
{
return Foo(ref retVal, true);
}
private bool Foo(ref int retVal, bool retValNeeded)
{
// ...
if (retValNeeded)
{
retVal = 5;
}
// ...
return true;
}
我会选择两种方法中的一种。如果您真的想要引用您的对象,您可以将其包装在 class 中。这些总是通过引用传递,因此您可以按照自己的方式进行修改,并且可以将其实例化为 null。
这是一个例子。
public class HolderObject
{
public string myStr {get; set;}
}
public class Program
{
public static void Main()
{
var xyz = new HolderObject() {
myStr = "1234"
};
Console.WriteLine(xyz.myStr);
FixString(xyz);
Console.WriteLine(xyz.myStr);
FixString();
Console.WriteLine(xyz.myStr);
}
private static bool FixString(HolderObject input = null)
{
if (input != null)
input.myStr = "test";
return true;
}
}
打印
1234
test
另一个解决方案是重载您的函数。
bool Foo()
{
// ...
return true;
}
bool Foo(ref int retVal = null)
{
// ...
if (retVal != null)
{
retVal = 5;
}
return Foo();
}
我真的不喜欢这个。实际上,我正在处理直接从 C++ 中提取的 C# 代码。嵌套 6 或 7 层的函数修改通过引用传递的对象。这很难阅读,如果您查看代码分析警告,它会建议您不要使用 ref 值。
如果可以的话,我会尽可能地避免传递 ref 并 return 返回已修改的值。或者传回一个包含您的布尔值和新值的对象。