繁体   English   中英

为什么在传递对象时使用 'ref' 关键字?

[英]Why use the 'ref' keyword when passing an object?

如果我将对象传递给方法,为什么要使用 ref 关键字? 这不是默认行为吗?

例如:

class Program
{
    static void Main(string[] args)
    {
        TestRef t = new TestRef();
        t.Something = "Foo";

        DoSomething(t);
        Console.WriteLine(t.Something);
    }

    static public void DoSomething(TestRef t)
    {
        t.Something = "Bar";
    }
}


public class TestRef
{
    public string Something { get; set; }
}

输出是“Bar”,这意味着该对象是作为引用传递的。

如果要更改对象是什么,请传递ref

TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);

void DoSomething(ref TestRef t)
{
  t = new TestRef();
  t.Something = "Not just a changed t, but a completely different TestRef object";
}

调用 DoSomething 后, t并不是指原来的new TestRef ,而是指一个完全不同的对象。

如果您想更改不可变对象(例如string )的值,这也可能很有用。 string一旦创建就不能更改它的值。 但是通过使用ref ,您可以创建一个函数,将字符串更改为另一个具有不同值的字符串。

除非需要,否则使用ref不是一个好主意。 使用ref使方法可以自由地更改其他参数,方法的调用者需要进行编码以确保他们处理这种可能性。

此外,当参数类型是对象时,对象变量始终充当对对象的引用。 这意味着当使用ref关键字时,您将获得对引用的引用。 这使您可以按照上面给出的示例中的描述进行操作。 但是,当参数类型是原始值(例如int )时,如果在方法内分配了这个参数,则传入的参数的值将在方法返回后更改:

int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10

void Change(ref int x)
{
  x = 5;
}

void WillNotChange(int x)
{
  x = 10;
}

您需要区分“通过值传递引用”和“通过引用传递参数/参数”。

我已经写了一篇相当长的关于这个主题的文章,以避免每次出现在新闻组上时都必须仔细写

在 .NET 中,当您将任何参数传递给方法时,都会创建一个副本。 在值类型中意味着您对值所做的任何修改都在方法范围内,并且在您退出方法时会丢失。

当传递一个引用类型时,也会创建一个副本,但它是一个引用的副本,即现在您在内存中有两个对同一个对象的引用。 因此,如果您使用引用来修改对象,它就会被修改。 但是,如果您修改引用本身——我们必须记住它是一个副本——那么任何更改也会在退出该方法时丢失。

正如人们之前所说,赋值是对引用的修改,因此丢失了:

public void Method1(object obj) {   
 obj = new Object(); 
}

public void Method2(object obj) {  
 obj = _privateObject; 
}

上述方法不会修改原始对象。

对您的示例进行一些修改

 using System;

    class Program
        {
            static void Main(string[] args)
            {
                TestRef t = new TestRef();
                t.Something = "Foo";

                DoSomething(t);
                Console.WriteLine(t.Something);

            }

            static public void DoSomething(TestRef t)
            {
                t = new TestRef();
                t.Something = "Bar";
            }
        }



    public class TestRef
    {
    private string s;
        public string Something 
        { 
            get {return s;} 
            set { s = value; }
        }
    }

由于 TestRef 是一个类(它们是引用对象),因此您可以更改 t 内部的内容,而无需将其作为 ref 传递。 但是,如果您将 t 作为 ref 传递,则 TestRef 可以更改原始 t 所指的内容。 即使它指向不同的对象。

使用ref你可以写:

static public void DoSomething(ref TestRef t)
{
    t = new TestRef();
}

并且 t 将在方法完成后更改。

将引用类型(例如List<T> )的变量(例如foo )视为持有“Object #24601”形式的对象标识符 假设语句foo = new List<int> {1,5,7,9}; 导致foo持有“对象#24601”(一个包含四个项目的列表)。 然后调用foo.Length将询问 Object #24601 的长度,它会响应 4,因此foo.Length将等于 4。

如果foo在不使用ref的情况下传递给方法,则该方法可能会更改 Object #24601。 由于此类更改, foo.Length可能不再等于 4。但是,方法本身将无法更改foo ,它将继续保存“Object #24601”。

foo作为ref参数传递将允许被调用的方法不仅可以更改 Object #24601,还可以更改foo本身。 该方法可能会创建一个新对象 #8675309 并将对该对象的引用存储在foo中。 如果这样做, foo将不再持有“Object #24601”,而是“Object #8675309”。

实际上,引用类型变量不包含“Object #8675309”形式的字符串; 他们甚至没有任何可以有意义地转换为数字的东西。 尽管每个引用类型变量都将保存一些位模式,但存储在这些变量中的位模式与它们所标识的对象之间并没有固定的关系。 代码无法从对象或对它的引用中提取信息,然后确定另一个引用是否标识了同一对象,除非代码持有或知道标识原始对象的引用。

这就像在 C 中传递一个指向指针的指针。在 .NET 中,这将允许您更改原始 T 所指的内容,尽管我个人认为如果您在 .NET 中这样做,您可能遇到了设计问题!

通过将ref关键字与引用类型一起使用,您实际上是在传递对引用的引用。 在许多方面,它与使用out关键字相同,但细微的差别在于不能保证该方法实际上会将任何内容分配给ref 'ed 参数。

ref模仿(或表现)为一个仅适用于两个范围的全局区域:

  • 呼叫者
  • 被调用者。

但是,如果您要传递一个值,则情况会有所不同。 您可以强制通过引用传递值。 例如,这允许您将整数传递给方法,并让该方法代表您修改整数。

如果将对象传递给方法,为什么要使用ref关键字? 这不是默认行为吗?

例如:

class Program
{
    static void Main(string[] args)
    {
        TestRef t = new TestRef();
        t.Something = "Foo";

        DoSomething(t);
        Console.WriteLine(t.Something);
    }

    static public void DoSomething(TestRef t)
    {
        t.Something = "Bar";
    }
}


public class TestRef
{
    public string Something { get; set; }
}

输出为“ Bar”,这表示该对象已作为参考传递。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM