【问题标题】:How to convert optional pointer argument from C++ code to C#如何将可选指针参数从 C++ 代码转换为 C#
【发布时间】:2015-09-13 12:13:54
【问题描述】:

我在 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 参数不能有默认值。

我该如何解决这个问题?

【问题讨论】:

  • 你可以使用像int?这样的可空类型。见:msdn.microsoft.com/en-us/library/2cf62fcy.aspx
  • @NathanOliver 请注意,虽然这适用于 ref int,但对于像 ref string 这样的参数,这不起作用
  • @xanatos 在 c# 中默认不是可以为空的字符串,因此您不需要使用 ??
  • @NathanOliver 但是你无法区分ref null string 的传递和“我不想传递string”......在C 中它将是char** str .你可以有str == NULL*str == NULL
  • 你可以简单地返回一个复合对象,Tuple<bool, int>如果你是懒惰的,如果你不是自定义类型。

标签: c# c++ reference arguments


【解决方案1】:

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;

请注意,我并不完全赞同我写的语义

更多的是你要求的东西,我给你的东西,没有问题

【讨论】:

  • 我喜欢你的免责声明。你能详细说明一下你对此的看法吗?您不必将其放在答案中,但是有没有更好的方法可以做到这一点,或者您喜欢 ref 解决方案吗?
  • @think 我完全支持卢卡斯模式,不包括不安全的模式。但我确实觉得他们或多或少都是黑客。我喜欢我的多一点,但我们仍然在谈论选择一种蔬菜而不是另一种。这张桌子上没有鱼或肉。
  • @认为重要的是要理解并非所有可以在 C 中轻松完成的事情都可以在 C# 中轻松完成。好的 C 模式并不总是好的 C# 模式。所以我们要理解、接受和继续,而不是试图模仿C的每一个角落和缝隙
  • @xantos 感谢这模仿了我的感受。我一直在研究 C++ 到 C# 的应用程序,但在这个阶段我们没有做足够的“重新思考”。并不是说以后不会出现,而是很多 C++ 适合 C#,而不是 C++ 重新设计为 C#。此应用程序的准确性要求使其在我们的时间表上对我们有意义。
  • 我喜欢你的评论“想想孩子们!”。我喜欢您使用 OptionalRef 类的解决方案,但我一直在寻找更简单的解决方案,例如 unsafe :P.
【解决方案2】:

最简单的方法就是写一个重载:

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;
}

或者,没有 cmets 中建议的 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;
}

【讨论】:

  • 我也在想同样的事情(我的第一个版本是相同的stackoverflow.com/revisions/31075073/1)。但是后来我注意到我们在这里丢失了信息...... C版本可以区分retVal*是否通过,并且可能需要进行一些繁重的计算才能返回它,而C#则无法做到...... C# version 是纯粹的语法糖,使方法更易于调用。
  • @xanatos 是的,我注意到原始代码中的if...我会写第二种方式。
  • 不安全的代码...请不要这样做...想想孩子们! :-) 如果你真的想要,你可以传递一个元素的数组......或者更好地简单地附加一个bool 参数。您已经用两种方法屏蔽了“真实”方法...在真实方法中添加bool
  • 我宁愿在两个公共重载适当设置的内部方法上添加另一个 bool fillOutput 参数。
  • @xanatos 是的,我是这样理解的 - 嘿,毕竟我没有写 goto :-)
【解决方案3】:

我会选择两种方法中的一种。如果您真的想要引用您的对象,您可以将其包装在一个类中。这些总是通过引用传递,因此您可以按照您想要的方式进行修改,并且可以将其实例化为 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 并返回已修改的值。或者将包含您的布尔值和新值的对象传回。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 2016-08-24
    • 1970-01-01
    • 1970-01-01
    • 2021-09-20
    • 2014-01-25
    相关资源
    最近更新 更多