C++/CLI 传递 String ^ 作为跟踪参考无法按预期工作

C++/CLI passing a String ^ as a tracking reference doesn't work as expected

我在 VS 2010 中使用 C++/CLI。感谢 this 的回答,我了解到在 C++/CLI 中你可以像这样通过引用传递对象

voin func(String ^ %string){
//modify string
}

我想做的是将一些参数传递给 GetInput 形式的构造函数。然后将它们从用户输入修改为这个表单。当表单关闭时,调用者表单将获得在 GetInput 表单中输入的值。

它对 List<double> 有效,但对 String^ 无效。这是代码:

public ref class GetInput : public System::Windows::Forms::Form
{
public:
    List<double>^ _data;
    String ^ _string_data;

    GetInput(List<double>^ %data, String ^ %string_data){
        _data = data;
        _string_data = string_data;
        //this code is here just for the sake of an example, 
        //it's actually in one of the methods
        _data->Add(0.5); 
        _string_data = "15";
    }
    //void buttonClickHandler where i actually want to modify data and string_data
}

List _data 被修改,以及传递的 data,但是当我修改 String _string_data 时,原始 string_data 保持不变。但是,如果我修改 string_data 本身 - 它工作正常。此外,跟踪引用不能是 class 的成员,因此将 _string_data 声明为引用无效。

明显的解决方法是传递由一个 String^ 组成的 List^,但我想知道为什么它不起作用以及是否有办法使我的方法起作用.来自原生 C++,这似乎是一种非常简单的方法——只需将指针传递给数据。但是 C++/ClI 跟踪引用有些不同。

我还读到 "A tracking reference is updated when the object moves on the garbage-collected heap." (source)。有人可以澄清这是什么意思吗?我怀疑这包含我的问题的答案,但我不太明白:)

更新: 基本上我想在 C++/CLI 中这样做:

class GetInput {
    string * s; 
    GetInput(string * caller_wants_this_modified){ 
        s = caller_wants_this_modified; 
    } 
    void method_to_modify_the_string(){ 
        (*s) = "New string value"; // modifies the string passed by the caller 
    } 
}

我需要将传递给构造函数的指针保存为 class 成员,然后在其中一个成员函数中更改此指针指向的数据。

But C++/ClI tracking references are something different

认为 "tracking reference" 有一些神奇的东西可能是让你在这里遇到麻烦的原因。 C++/CLI 语言和文档强调了这一点,它是为本地 C++ 程序员编写的。 实际上并没有什么不同。

它只是一个指针。

它的行为也完全像一个指针,无论是在处理器上还是在语法上。关于它的唯一 "special" 是垃圾收集器可以找回它。必要的,这样它才能完成它的工作。 C++/CLI 设计者选择使用 ^ 而不是 * 因为它们确实有限制。普通的,例如,您不能使其成为非托管 class 的成员。 GC 无法知道它在内存中的位置,因此无法完成它的工作。

你的问题很简单,你有两个指针。 _string_datastring_data。请注意,您选择的名称并不能完全帮助您正确选择。您更新了 _string_data 指针,赋值后它现在指向“15”。但是不是你通过引用传递的指针,它仍然指向原始字符串。因此,当然不会将任何更改传播回调用方。不知道是什么意思,但很随意:

  _string_data = "15";
  string_data = _string_data;

现在调用者还将看到其引用更新为“15”。现在两个指针都指向同一个对象。

请注意,您对 List<> 参数的理解也有误。您没有更新data指针。您也不需要,您没有创建新的 List 对象。您的代码只修改了对象,而不是指向对象的指针。所以通过引用传递它是完全没有必要的。


还有一个细节与这个问题有关。请注意,您通过调用其 Add() 方法修改了 List<> 对象。但是你不能对 String 做同样的事情。它是immutable,none它的方法和属性允许你修改它的内容。这给了它非常理想的行为,字符串始终是线程安全的,并且始终可以安全地作为参数传递给方法。确保它永远不会被修改。它的行为就像一个值。

或者换句话说,您必须通过引用传递字符串指针以将更改传播回调用者。

请注意 List<> 不是一成不变的,您的 Add() 调用彻底改变了对象。使用您的 class 的客户端程序员不太可能期望发生这种情况。 C++ 中的 const 关键字是表达差异的一种很好的方式。然而,它在 C++/CLI 中的表现并不好,其他 .NET 语言对它一无所知。您可以在声明中使用 const,它不能在运行时强制执行。