如何传递string类型的属性

时间:2012-05-22 09:16:14

标签: c#-4.0 properties

我有以下代码(请注意下面的代码不会更新属性)

private void queryResultsFilePath_Click(object sender, EventArgs e)
{
        Library.SProc.Browse browser = new Browse();
        browser.selectFile(QueryResultFilePath);
}

public class Browse
{

    public void selectFile(string propertyName)
    {
        ...
        propertyName = browserWindow.FileName;  
    }
}

现在我意识到我需要更改第二个方法,以便它返回一个字符串并手动将其分配给第一个示例中的属性。

我不确定的是,我认为当我将ref类型指定为方法的实际参数时,其在堆栈上的值的副本(即其在堆中的内存地址)被复制到新位置上方法形式参数的堆栈,因此它们都指向堆上的相同内存地址。因此,当我更改形式参数的值时,它实际上会更改存储在堆上的值,从而更改实际参数值。

显然我遗漏了一些东西,因为我必须返回一个字符串并手动将其分配给属性。如果有人能够指出我误解了什么,那就是欣赏它。

感谢。

3 个答案:

答案 0 :(得分:3)

我相信这里缺少的部分是:字符串是不可变的。

虽然你通过引用传递它,但是只要有人试图改变字符串,就会创建一个新的字符串,保留旧的字符串。

我相信它是唯一强制不变性的参考类型。

来自MSDN

  

字符串是不可变的 - 字符串对象的内容不能   在创建对象后更改,尽管语法可以实现   好像你可以这样做。例如,当您编写此代码时,   编译器实际上创建了一个新的字符串对象来保存新的   字符序列,并将新对象分配给b。该   字符串“h”然后有资格进行垃圾收集。

进一步阅读:

http://social.msdn.microsoft.com/Forums/en/netfxbcl/thread/e755cbcd-4b09-4a61-b31f-e46e48d1b2eb

如果您希望方法“更改”来电者的字符串,那么您可以使用ref关键字模拟

public void SelectFile(ref string propertyName)
{
    propertyName = browserWindow.FileName;  
}

在此示例中,参数propertyName将在方法中分配,因为使用了ref,这也会更改调用者指向的字符串。请注意,仍然强制执行不变性。 propertyName用于指向字符串A,但现在赋值后指向字符串B - 旧字符串A现在未被引用并将被垃圾收集(但重要的是仍然存在并且未被更改 - 不可变)。如果未使用ref关键字,则调用者仍将指向A,方法将指向B.但是,由于使用了ref关键字,调用者变量现在指向字符串B. / p>

这与以下示例的效果相同:

static void Main(string[] args)
{
    MyClass classRef = new MyClass("A");
    PointToANewClass(ref classRef);
    // classRef now points to a brand new instance containing "B".
}

public static void PointToANewClass(ref MyClass classRef)
{
    classRef = new MyClass("B");
}

如果你尝试使用上面的而不是 ref关键字,classRef仍会指向包含“A”的对象,即使该类是通过引用传递的。

不要混淆字符串语义和ref语义。并且也不要在通过引用和赋值传递内容之间混淆。从技术上说,Stuff 从不 通过引用传递, 指向堆上对象的指针通过值传递 - 因此引用类型上的ref具有上面指定的行为。因此,不使用ref将不允许在调用者和方法之间“共享”新的赋值,该方法已经收到了自己的 copy 指向堆上对象的指针,解除引用指针具有通常的效果(查看相同的底层对象),但是指定指针不会影响指针的调用者副本。

答案 1 :(得分:2)

我非常感谢Adam Houldsworth,因为我终于明白了.NET框架如何使用参考参数以及字符串会发生什么。

在.NET中有两种数据类型:

  • 值类型:基本类型,如int,float,bool等
  • 引用类型:所有其他对象,包括字符串

对于引用类型,对象存储在堆中,而变量只保存指向此对象的引用。您可以通过引用访问对象的属性并进行修改。将其中一个变量作为参数传递时,指向同一对象的引用副本将传递给方法主体。因此,当您访问和修改属性时,您将修改存储在堆上的相同对象。即,这个类是一个引用对象:

    public class ClassOne
    {
        public string Desc { get; set; }
    }

当你这样做时

    ClassOne one = new { Desc = "I'm a class one!" };

引用one指向的堆上有一个对象。如果你这样做:

    one.Desc = "Changed value!";

堆上的对象已被修改。如果将此引用作为参数传递:

    public void ChangeOne(ClassOne one)
    {
        one.Desc = "Changed value!"
    }

堆上的原始对象也发生了变化,因为one持有指向堆上同一对象的原始引用的副本。

但如果你这样做:

    public void ChangeOne(ClassOne one)
    {
        one = new ClassOne { Desc ="Changed value!" };
    }

原始对象不变。这是因为one是引用的副本,它现在指向不同的对象。

如果您通过引用明确传递它:

    public void ChangeOne(ref ClassOne one)
    {
        one = new ClassOne { Desc ="Changed value!" };
    }
此方法中的

one不是外部引用的副本,而是引用本身,因此,原始引用现在指向此新对象。

字符串是不可变的。这意味着您无法更改字符串。如果您尝试这样做,则会创建一个新字符串。所以,如果你这样做:

  string s = "HELL";
  s = s + "O";

第二行创建一个新的字符串实例,其值为" HELLO" " HELL"被遗弃在堆上(留下来进行垃圾收集)。

因此,如果您将其作为参数传递给它,则无法更改它:

    public void AppendO(string one)
    {
        one = one + "O";
    }

    string original =  "HELL";
    AppendO(original);

保留original字符串。函数内部的代码创建一个新对象,并将其分配给一个原始引用的副本。但是原创一直指向" HELL"。

对于值类型,当它们作为参数传递给函数时,它们按值传递,即函数接收原始值的副本。因此,对函数体内对象所做的任何修改都不会影响函数外的原始值。

问题是,虽然字符串是引用类型,它看起来好像的行为类似于值类型(这适用于比较,传递参数等等)上)。

但是,如上所述,可以使编译器使用ref关键字通过引用传递引用类型。这也适用于字符串。

您可以查看此代码,并且您会看到该字符串已被修改(这也适用于intfloat或任何其他值类型):

    public static class StringTest
    {
        public static void AppednO(ref string toModify)
        {
            toModify = toModify + "O";
        }
    }

    // test:
    string hell = "HELL";
    StringTest.AppendO(ref hell);
    if (hell == "HELLO")
    {
       // here, hell is "HELLO"
    }

请注意,为了避免错误,当您将参数定义为ref时,您还必须使用此修饰符传递参数。

无论如何,对于这种情况(以及类似情况),我建议您使用更自然的功能语法:

  var hell = StringTest.AppendO(hell);

(当然,在这种情况下,该函数将具有此签名和相应的实现:

  public static string AppendO(string value)
  {
      return value + "O";
  }

如果您要对字符串进行许多更改,则应使用StringBuilder类,该类使用"可变字符串"。

How a property, of type string, is passed

答案 2 :(得分:1)

字符串是不可变的,因此您将它们的副本传递给方法。这意味着副本会更改但原始参数保持不变。