如何将可选指针参数从 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 返回已修改的值。或者传回一个包含您的布尔值和新值的对象。