C#——ref

C#——ref

ref 关键字指示按引用传递的值。 它用在四种不同的上下文中:

1.在方法签名和方法调用中,按引用将参数传递给方法。
2.在方法签名中,按引用将值返回给调用方。
3.在成员正文中,指示引用返回值是否作为调用方欲修改的引用被存储在本地,或在一般情况下,局部变量按引用访问另一个值。
4.在 struct 声明中声明 ref struct 或 readonly ref struct。

按引用传递参数

在方法的参数列表中使用 ref 关键字时,它指示参数按引用传递,而非按值传递。 ref 关键字让形参成为实参的别名,这必须是变量。 换而言之,对形参执行的任何操作都是对实参执行的。 例如,如果调用方传递本地变量表达式或数组元素访问表达式,所调用方法会替换 ref 参数引用的对象,然后,当该方法返回时,调用方的本地变量或数组元素将开始引用新对象。

不要混淆通过引用传递的概念与引用类型的概念。 这两种概念是不同的。 无论方法参数是值类型还是引用类型,均可由 ref 修改。 当通过引用传递时,不会对值类型装箱。

若要使用 ref 参数,方法定义和调用方法均必须显式使用 ref 关键字,如下面的示例所示。

void Method(ref int refArgument)
{
    refArgument = refArgument + 44;
}

int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45

传递到 ref 或 in 形参的实参必须先经过初始化,然后才能传递。 这与 out 形参不同,在传递之前,不需要显式初始化该形参的实参。

类的成员不能具有仅在 ref、in 或 out 方面不同的签名。 如果类型的两个成员之间的唯一区别在于其中一个具有 ref 参数,而另一个具有 out 或 in 参数,则会发生编译器错误。 例如,以下代码将不会编译。

class CS0663_Example
{
    // Compiler error CS0663: "Cannot define overloaded
    // methods that differ only on ref and out".
    public void SampleMethod(out int i) { }
    public void SampleMethod(ref int i) { }
}

但是,当一个方法具有 ref、in 或 out 参数,另一个方法具有值参数时,则可以重载方法,如下面的示例所示。

class RefOverloadExample
{
    public void SampleMethod(int i) { }
    public void SampleMethod(ref int i) { }
}

在其他要求签名匹配的情况下(如隐藏或重写),in、ref 和 out 是签名的一部分,相互之间不匹配。

属性不是变量。 它们是方法,不能传递到 ref 参数。

不能将 ref、in 和 out 关键字用于以下几种方法:

1. 异步方法,通过使用 async 修饰符定义。
2. 迭代器方法,包括 yield return 或 yield break 语句。

此外,扩展方法具有以下限制:

1.不能对扩展方法的第一个参数使用 out 关键字。
2.当参数不是结构或是不被约束为结构的泛型类型时,不能对扩展方法的第一个参数使用 ref 关键字。
3.除非第一个参数是结构,否则不能使用 in 关键字。 即使约束为结构,也不能对任何泛型类型使用 in 关键字。

按引用传递参数:示例

前面的示例按引用传递值类型。 还可使用 ref 关键字按引用传递引用类型。 按引用传递引用类型使所调用方能够替换调用方中引用参数引用的对象。 对象的存储位置按引用参数的值传递到方法。 如果更改参数存储位置中的值(以指向新对象),你还可以将存储位置更改为调用方所引用的位置。 下面的示例将引用类型的实例作为 ref 参数传递。

class Product
{
    public Product(string name, int newID)
    {
        ItemName = name;
        ItemID = newID;
    }

    public string ItemName { get; set; }
    public int ItemID { get; set; }
}

private static void ChangeByReference(ref Product itemRef)
{
    // Change the address that is stored in the itemRef parameter.
    itemRef = new Product("Stapler", 99999);

    // You can change the value of one of the properties of
    // itemRef. The change happens to item in Main as well.
    itemRef.ItemID = 12345;
}

private static void ModifyProductsByReference()
{
    // Declare an instance of Product and display its initial values.
    Product item = new Product("Fasteners", 54321);
    System.Console.WriteLine("Original values in Main.  Name: {0}, ID: {1}\n",
        item.ItemName, item.ItemID);

    // Pass the product instance to ChangeByReference.
    ChangeByReference(ref item);
    System.Console.WriteLine("Back in Main.  Name: {0}, ID: {1}\n",
        item.ItemName, item.ItemID);
}

// This method displays the following output:
// Original values in Main.  Name: Fasteners, ID: 54321
// Back in Main.  Name: Stapler, ID: 12345

引用返回值

引用返回值(或 ref 返回值)是由方法按引用向调用方返回的值。 即是说,调用方可以修改方法所返回的值,此更改反映在调用方法中的对象的状态中。

使用 ref 关键字来定义引用返回值:

在方法签名中。 例如,下列方法签名指示 GetCurrentPrice 方法按引用返回了 Decimal 值。

public ref decimal GetCurrentPrice()

在 return 标记和方法的 return 语句中返回的变量之间。 例如:

return ref DecimalArray[0];

为方便调用方修改对象的状态,引用返回值必须存储在被显式定义为 ref 局部变量的变量中。

下面是一个更完整的 ref 返回示例,同时演示方法签名和方法主体。

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

所调用方法还可能会将返回值声明为 ref readonly 以按引用返回值,并坚持调用代码无法修改返回的值。 调用方法可以通过将返回值存储在本地 ref readonly 变量中来避免复制该值。

ref 局部变量

ref 局部变量用于指代使用 return ref 返回的值。 无法将 ref 局部变量初始化为非 ref 返回值。 也就是说,初始化的右侧必须为引用。 任何对 ref 本地变量值的修改都将反映在对象的状态中,该对象的方法按引用返回值。

在变量声明前或在方法(该方法将按引用返回值)调用前使用 ref 关键字定义 ref 局部变量。

例如,下列语句定义名为 GetEstimatedValue 的方法返回的 ref 局部变量值:

ref decimal estValue = ref Building.GetEstimatedValue();

可通过相同方式按引用访问值。 在某些情况下,按引用访问值可避免潜在的高开销复制操作,从而提高性能。 例如,以下语句显示用户可如何定义一个用于引用值的 ref 局部变量值。

ref VeryLargeStruct reflocal = ref veryLargeStruct;

在这两个示例中,必须在两个位置同时使用 ref 关键字,否则编译器将生成错误 CS8172:“无法使用值对按引用变量进行初始化”。

从 C# 7.3 开始,foreach 语句的迭代变量可以是 ref local 变量,也可以是 ref readonly local 变量。

此外,从 C# 7.3 开始,可以使用 ref 赋值运算符重新分配 ref local 或 ref readonly local 变量。

Ref readonly 局部变量

Ref readonly 局部变量用于指代在其签名中具有 ref readonly 并使用 return ref 的方法或属性返回的值。 ref readonly 变量将 ref 本地变量的属性与 readonly 变量结合使用:它是所分配到的存储的别名,且无法修改。

ref 返回值和 ref 局部变量示例

下列示例定义一个具有两个 String 字段(Title 和 Author)的 Book 类。 还定义包含 Book 对象的专用数组的 BookCollection 类。 通过调用 GetBookByTitle 方法,可按引用返回个别 book 对象。

public class Book
{
    public string Author;
    public string Title;
}

public class BookCollection
{
    private Book[] books = { new Book { Title = "Call of the Wild, The", Author = "Jack London" },
                        new Book { Title = "Tale of Two Cities, A", Author = "Charles Dickens" }
                       };
    private Book nobook = null;

    public ref Book GetBookByTitle(string title)
    {
        for (int ctr = 0; ctr < books.Length; ctr++)
        {
            if (title == books[ctr].Title)
                return ref books[ctr];
        }
        return ref nobook;
    }

    public void ListBooks()
    {
        foreach (var book in books)
        {
            Console.WriteLine($"{book.Title}, by {book.Author}");
        }
        Console.WriteLine();
    }
}

调用方将 GetBookByTitle 方法所返回的值存储为 ref 局部变量时,调用方对返回值所做的更改将反映在 BookCollection 对象中,如下例所示。

var bc = new BookCollection();
bc.ListBooks();

ref var book = ref bc.GetBookByTitle("Call of the Wild, The");
if (book != null)
    book = new Book { Title = "Republic, The", Author = "Plato" };
bc.ListBooks();
// The example displays the following output:
//       Call of the Wild, The, by Jack London
//       Tale of Two Cities, A, by Charles Dickens
//
//       Republic, The, by Plato
//       Tale of Two Cities, A, by Charles Dickens
  • 15
    点赞
  • 100
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值