C#ref混乱

时间:2010-04-29 15:32:00

标签: c#

我有一种困惑,当我在另一个类的构造函数中通过引用传递变量时,在通过refrence传递该对象之后,我使用new关键字重新创建了refrence对象。

现在我传递refrenced对象的类不会反映更新的数据。 上述问题的一个例子如下所示:

Refrence传递的对象:

public class DummyObject
{
    public string Name = "My Name";

    public DummyObject()
    { }
}

传递参考的类:

public partial class Form1 : Form
{
    // Object to be passed as refrence
    DummyObject dummyObject = new DummyObject();

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        // assigning value
        dummyObject.Name = "I am Dummy";

        // Passing object
        Form2 frm = new Form2(ref dummyObject);
        frm.Show();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        // Displaying Name
        MessageBox.Show(this.dummyObject.Name);
    }

    private void button3_Click(object sender, EventArgs e)
    {
        // Assigning new object
        this.dummyObject = new DummyObject();

        // Changing Name Variable
        this.dummyObject.Name = "I am Rechanged";

        // Displaying Name
        MessageBox.Show(this.dummyObject.Name);
    }
}

通过引用传递Object的类:

public partial class Form2 : Form
{
    private DummyObject dummyObject = null;

    public Form2(ref DummyObject DummyObject)
    {
        InitializeComponent();

        this.dummyObject = DummyObject;
        this.dummyObject.Name = "I am Changed";
    }

    private void button2_Click(object sender, EventArgs e)
    {
        MessageBox.Show(this.dummyObject.Name);
    }
}

当我在表单1中重新设置对象并在表单2中显示其值时,它仍显示“我已更改”而不是“我被改变”。

如何保持数据同步?

6 个答案:

答案 0 :(得分:4)

我认为你误解了ref keyord的作用。当您将dummyObject传递给Form2的构造函数时,Form2不会将它自己的dummyObject字段与Form1中的dummyObject字段链接。

您可以通过避免创建DummyObject的新实例或使Form1在创建新实例时告诉Form2来保持数据同步。

就目前而言,你实际上根本不需要使用ref关键字,如果要删除它,它将没有任何区别。

答案 1 :(得分:2)

您不能以这种方式保持变量同步;没有ref实例或静态变量的概念,只有ref个参数。 dummyObject实例变量确实(并且将始终)表示不同的内存槽。您所做的只是将DummyObject参数中的值复制到dummyObject;你所做的一切都不会受到参数是否被声明为ref的影响。

典型的方法是将dummyObject的值公开为Form2上的属性。

public DummyObject DummyObject
{
    get { return dummyObject; }
    set 
    { 
        dummyObject = value;

        // any other code, if any, that might need to execute 
        // when the value is changed
    }
}

但这意味着您需要保留Form2的实例,以便更改属性的值。

另一个选项,虽然有点复杂,但会传递一个包含该属性的包装类,而不是将其添加到表单中。

public class DummyWrapper
{
    public DummyObject DummyObject { get; set; }
}

然后,您更改表单以使用DummyWrapper而不是DummyObject,然后在您想要获取或设置值时访问dummyWrapper.DummyObject属性。只要您只更改DummyWrapper.DummyObject属性的值而不是DummyWrapper的实际值,那么您将指向同一个实例。

例如:

public partial class Form2 : Form 
{ 
    private DummyWrapper dummyWrapper = null; 

    public Form2(DummyWrapper dummyWrapper) 
    { 
        InitializeComponent(); 

        this.dummyWrapper = dummyWrapper;
        this.dummyWrapper.DummyObject.Name = "I am Changed"; 
    } 

    private void button2_Click(object sender, EventArgs e) 
    { 
        MessageBox.Show(this.dummyWrapper.DummyObject.Name); 
    } 
} 

答案 2 :(得分:2)

你误解了“ref”的作用。我必须解释“ref”的最佳方法是为变量创建别名。当你说

void M(ref int x)
{
    Console.WriteLine(x);
    x = 10;
}
...
int y = 123;
M(ref y);

你在电话网站上说的是“x现在是变量y的另一个名字”。也就是说,这就像你简单地说

一样
int y = 123;
Console.WriteLine(y);
y = 10;

x是y的别名。不幸的是,我们选择“ref”这个词来表示“为一个变量做一个别名”,因为这很令人困惑,但这就是语言设计者所选择的。

现在,如果你说

void N(int z)
{
    Console.WriteLine(z);
    z = 10;
}
...
int y = 123;
N(y);

不会使z成为y的别名。该代码与

相同
int y = 123;
int z = y;
Console.WriteLine(z);
z = 10;

不会改变y的值,因为z和y是两个不同的变量,而x和y是相同变量的两个不同名称

答案 3 :(得分:0)

ref关键字不会为您提供此功能。相反,它允许您将传递的变量重新分配给新实例,但仅限于方法的上下文中。澄清:

void SomeMethodWithRef(ref DummyObject dummy)
{
    dummy = new DummyObject(){Name="Modified"};
}

void CallingMethod()
{
    DummyObject obj = new DummyObject(){Name="Original"};
    SomeMethodWithRef(ref obj);
    // at this point, obj has Name="Modified";
}

但是,在您的情况下,您将对象引用存储为具有完全不同语义的字段 - 重新分配该字段与重新分配原始变量不同。

要保持数据同步,请在进行更改时对同一实例进行操作(例如this.dummyObject.Name = "I am changed";),而不是创建一个全新的实例,或者您需要提供一种传播机制表单之间的更改(例如,让表单实现INotifyPropertyChanged并订阅彼此的属性更改通知)。

答案 4 :(得分:0)

它不能做你想要的是Form1.button3_click的第一行,创建第二个DummyObject实例。此新实例永远不会发送到Form2,因此Form2只有旧实例的副本。现在Form1和Form2有不同的对象,因此有不同的消息。

只有当Form2的构造函数想要在OUT路径上为参数分配新实例时,才会在您的示例中使用ref关键字(它与out参数不同,因为它是双向的而不是一个 - 办法)。这样的赋值会改变Form1.dummyObject字段的值。

答案 5 :(得分:0)

我已使用Adam Robinson建议的属性重新考虑我的应用程序以进行同步。现在我的代码已经如下所示进行了更改并产生了所需的结果。

包含需要同步的数据的类:

public class DummyObject
{
    public string Name = "My Name is this.";        

    public DummyObject()
    { }        
}

创建新的Inctance& amp;将其重新分配给Property:

public partial class Form1 : Form
{
    // Instance of DummyObject
    DummyObject dummyObject = new DummyObject();

    // Create Instance of Form2
    Form2 frm2 = new Form2();

    public Form1()
    {
        InitializeComponent();            

        // Assign DummyObject to Form2.DummyObject property
        frm2.DummyObject = this.dummyObject;

        // Change Form2 DummyObject.Name
        frm2.DummyObject.Name = "I am changed for Form2.";

        // Display Form2
        frm2.Show();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show(this.dummyObject.Name);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        // Change Name of Form1 DummyObject
        this.dummyObject.Name = "I am changed from Form1.";
    }

    private void button3_Click(object sender, EventArgs e)
    {
        // Assign new Instance
        this.dummyObject = new DummyObject();

        // Change Name value
        this.dummyObject.Name = "I am rechanged from Form1.";

        // Reassign Form2.DummyObject the newly created instance
        // for synchronization purposes
        this.frm2.DummyObject = this.dummyObject;         
    }       
}

按属性共享对象的类:

public partial class Form2 : Form
{
    private DummyObject dummyObject;

    public Form2()
    {
        InitializeComponent();
    }

    public DummyObject DummyObject
    {
        get { return this.dummyObject; }
        set { this.dummyObject = value; }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show(this.dummyObject.Name);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        this.dummyObject.Name = "I am changed from Form2.";            
    }
}

感谢每个人的快速反应。