首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >字符串类型的属性的传递方式

字符串类型的属性的传递方式
EN

Stack Overflow用户
提问于 2012-05-22 17:16:14
回答 3查看 76关注 0票数 2

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

代码语言:javascript
运行
复制
private void queryResultsFilePath_Click(object sender, EventArgs e)
{
        Library.SProc.Browse browser = new Browse();
        browser.selectFile(QueryResultFilePath);
}

代码语言:javascript
运行
复制
public class Browse
{

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

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

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

显然,我遗漏了一些东西,因为我必须返回一个字符串并手动将其分配给属性。如果有人能指出我误解了什么,我会很感激的。

谢谢。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-05-22 17:18:21

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

尽管您通过引用传递它,但只要有任何东西试图改变字符串,就会创建一个新的字符串,而保留旧的字符串不变。

我相信它是唯一一个强制不可变的引用类型。

来自MSDN

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

进一步阅读:

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

如果您希望该方法“更改”调用者的字符串,则可以使用ref关键字进行模拟:

代码语言:javascript
运行
复制
public void SelectFile(ref string propertyName)
{
    propertyName = browserWindow.FileName;  
}

在此示例中,由于使用了ref,因此将在方法中为参数propertyName赋值,这也会更改调用者所指向的字符串。请注意,这里的不变性仍然是强制的。propertyName过去指向字符串A,但赋值之后现在指向字符串B-旧的字符串A现在是未引用的,并且将被垃圾回收(但重要的是,它仍然存在并且没有改变-不可变)。如果未使用ref关键字,则调用方仍将指向A,而方法将指向B。但是,由于使用了ref关键字,因此callers变量现在指向字符串B。

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

代码语言:javascript
运行
复制
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");
}

如果在不使用关键字的情况下尝试上面的,即使类是通过引用传递的,classRef仍然会指向一个包含"A“的对象。

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

票数 3
EN

Stack Overflow用户

发布于 2012-05-22 18:00:02

我真的很感谢Adam Houldsworth,因为我终于理解了.NET框架如何使用引用参数以及字符串发生了什么。

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

  • 值类型:原始类型,如整数、浮点数、布尔值等
  • 引用类型:所有其他对象,包括string

引用类型为的情况下,对象存储在堆中,并且变量仅保存指向此对象的引用。您可以通过引用访问对象的属性并对其进行修改。当您将此变量之一作为参数传递时,指向同一对象的引用的副本将传递给方法体。因此,当你访问和修改属性时,你是在修改存储在堆中的相同对象。也就是说,这个类是一个引用对象:

代码语言:javascript
运行
复制
    public class ClassOne
    {
        public string Desc { get; set; }
    }

当你这样做的时候

代码语言:javascript
运行
复制
    ClassOne one = new { Desc = "I'm a class one!" };

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

代码语言:javascript
运行
复制
    one.Desc = "Changed value!";

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

代码语言:javascript
运行
复制
    public void ChangeOne(ClassOne one)
    {
        one.Desc = "Changed value!"
    }

堆上的原始对象也被更改,因为one持有指向堆上同一对象的原始引用的副本。

但是如果你这样做:

代码语言:javascript
运行
复制
    public void ChangeOne(ClassOne one)
    {
        one = new ClassOne { Desc ="Changed value!" };
    }

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

如果通过引用显式传递它:

代码语言:javascript
运行
复制
    public void ChangeOne(ref ClassOne one)
    {
        one = new ClassOne { Desc ="Changed value!" };
    }

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

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

代码语言:javascript
运行
复制
  string s = "HELL";
  s = s + "O";

第二行创建了一个新的string实例,它的值为"HELLO“,而”地狱“被丢弃在堆上(留下等待垃圾收集)。

因此,如果您像这样将其作为参数传递,则不可能对其进行更改:

代码语言:javascript
运行
复制
    public void AppendO(string one)
    {
        one = one + "O";
    }

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

original字符串保持原样。函数内部的代码创建一个新对象,并将其分配给一个对象,该对象是原始引用的副本。但original一直指向“地狱”。

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

问题是,虽然string是一个引用类型,但它看起来就像是一个值类型(这适用于比较、传递参数等)。

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

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

代码语言:javascript
运行
复制
    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时,还必须传递带有此修饰符的参数。

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

代码语言:javascript
运行
复制
  var hell = StringTest.AppendO(hell);

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

代码语言:javascript
运行
复制
  public static string AppendO(string value)
  {
      return value + "O";
  }

如果你要对一个字符串做很多修改,你应该使用StringBuilder类,它可以处理“可变字符串”。

How a property, of type string, is passed

票数 2
EN

Stack Overflow用户

发布于 2012-05-22 17:18:24

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10699103

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档