1.默认情况下,C#假定所有的方法参数传递都是传值的。

如下面的方法:

public static void Main(string[] args)
{
int val = 5; //调用AddValue方法,aVal会重新拷贝一份val的值(即aVal为val的一个实例副本),方法内部的操作并不会改变val的值。
AddValue(val); //val值还是5,并没有加1
Console.WriteLine(val);
Console.ReadLine();
} public static void AddValue(int aVal)
{
aVal = aVal + 1;
}
 
如果期望在调用AddValue方法以后,val加一,有两种方案:
1、修改AddValue,将修改后的值作为返回值,并赋值给val。
2、使用ref关键字、out关键字。传入参数时,不再传入一个原来类型的拷贝,而是直接传入原有值类型的地址(类似于引用类型) ,这样在方法内部的任意修改都会影响到传入的值类型。
 
2.引用类型的ref、out
引用类型作为方法参数传入时,对象的引用(或者指向该对象的指针)会传入方法参数,在方法内部对该对象的修改,被调用者也能直接看到。
这样是不是就不需要ref、out关键字了呢?
eg:
其实,引用类型作为方法参数传入时,理论上还是会新建一个变量,该变量指向和原有引用类型的指向相同。
 
如果在调用方法内部修改了引用类型的属性,使用ref和不使用ref没有区别,参考代码为:
public static void Main(string[] args)
{
User user = new User(); user.Name = "Lisa"; //不使用关键字ref,在调用方法内部修改User对象的属性,也会影响传入参数User.
//因为此时的User和方法参数aUser都指向同一份引用。
ChangeUser(user);
Console.WriteLine(user.Name); //使用关键字ref,在调用方法内部修改User对象的属性,也会影响传入参数User
//因为此时的User和方法参数aUser是同一个对象。
ChangeUser(ref user);
Console.WriteLine(user.Name); Console.ReadLine();
} public static void ChangeUser(User aUser)
{
aUser.Name = "Alan1";
} public static void ChangeUser(ref User aUser)
{
aUser.Name = "Alan2";
}
输出结果为:
Alan1
Alan2
此时看不出不使用ref和使用ref的区别,同样对在方法内部修改User的Name属性,效果一样
 
 
如果在调用方法内部修改了引用类型的指向,使用ref和不使用ref有区别,参考代码为:
public static void Main(string[] args)
{
User user = new User(); //不使用关键字ref,在调用方法内部修改User对象的引用,不会影响传入参数User.
//因为在栈中有两个独立的指针user、aUser
ChangeUser(user);
if (user != null)
{
Console.WriteLine("user1 is not null");
}
else
{
Console.WriteLine("user1 is null");
} //使用关键字ref,在调用方法内部修改User对象的引用,会影响传入参数User.
//因为在栈中仅有一个指针user
ChangeUser(ref user);
if (user != null)
{
Console.WriteLine("user2 is not null");
}
else
{
Console.WriteLine("user2 is null");
} Console.ReadLine();
} public static void ChangeUser(User aUser)
{
aUser = null;
} public static void ChangeUser(ref User aUser)
{
aUser = null;
}
输出结果为:
user1 is not null
user2 is null
此时能够明细看错不使用ref和使用ref的区别,同样对在方法内部赋值User ==null,效果不一样
 
引用关系图为:
引用类型Val本身
 
 
调用Change(aVal)方法以后:
 
调用Change(ref aVal)方法以后:
 
从上可知,如果需要真正的指向同一个引用,在方法内部的任何改变都会影响到传入参数,还是需要使用ref和out关键字。
 
3.从CLR来说,ref和out本身是一样的,都导致传递指向实例的指针。
唯一的区别是:
ref参数需要调用者在调用方法之前初始化参数的值(强制要求,不赋值编译不通过)
out参数不指望调用者在调用方法之前初始化参数默认值,即使调用者之前赋值,在调用方法内部也会重新赋值(即调用者的赋值会被替换,赋值无效,所以不建议调用者提前赋值)。
 
4.其它
a.允许对方法进行ref和out进行重载(即认为使用ref/out跟不使用ref/out函数签名不一致)
以下方法能够编译通过。

public static void ChangeUser(User aUser)
{
    aUser = null;
}

public static void ChangeUser(ref User aUser)
{
    aUser = null;
}

编译通过

但是,如果两个方法只有ref和out的区别,是不允许的。因为两个方法签名的元数据是完全相同的。
以下方法编译不过

public static void ChangeUser(ref User aUser)
{
    aUser = null;
}

public static void ChangeUser(out User aUser)
{
    aUser = null;
}

编译不过:

错误: “ChangeUser”不能定义仅在 ref 和 out 上有差别的重载方法

b.使用ref/out关键字时,方法定义和调用时参数类型必须一致。

 
 

C#基础-ref、out的更多相关文章

  1. [C#基础]ref和out的区别

    在C#中通过使用方法来获取返回值时,通常只能得到一个返回值.因此,当一个方法需要返回多个值的时候,就需要用到ref和out,那么这两个方法区别在哪儿呢? MSDN:       ref 关键字使参数按 ...

  2. 10. react 基础 ref 的使用 及 React 16 的生命周期函数 及 生命周期函数使用场景

    一. ref 的使用 ( 直接获取 DOM 元素 ) 在 input 标签上 可以使用 ref 属性 获取当前DOM节点 eg: import React , { Component, Fragmen ...

  3. [C#基础]ref和out的使用

    在C#中如果需要把值类型转换成引用类型传递其他方法中并使其原来值发生改变,使用 ref 和 out 转换成引用类型传递. 1. ref : 使用ref之前需要定义变量并初始化(必须初始) class ...

  4. (C#基础) ref 和out练习

    对于C#中这两个关键字的用法,常常混淆,有点不清楚,今天又一次看到.遂把它们都记录下来,希望能有所用.这些都是他人写的,我只是搬过来一次,加深印象. 代码 using System; using Sy ...

  5. C#基础--Ref与Out区别

    两者都是按地址传递的,使用后都将改变原来参数的数值. class Program { static void Main(string[] args) { int num = 1; Method(ref ...

  6. vue基础 ref的作用

    1.  ref 获取dom元素,除了能获取dom元素也能获取组件dom,   组件通信:        在父组件中直接调用ref定义的组件的数据或者方法 <div id="app&qu ...

  7. [Laravel] 14 - REST API: Laravel from scratch

    前言 一.基础 Ref: Build a REST API with Laravel API resources Goto: [Node.js] 08 - Web Server and REST AP ...

  8. Python :数据结构

    LearnPython :数据结构 .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .labe ...

  9. Java.Annotations

    Annotation 0. Annotation Tricks http://developer.android.com/reference/java/lang/annotation/Annotati ...

随机推荐

  1. header('Content-type:text/html;charset = utf-8');出现中文乱码

    header('Content-type:text/html;charset = utf-8'); "="两旁不能留空格,必须紧密连写,否则出现乱码;

  2. Replace JSON.NET with ServiceStack.Text in ASP.NET Web API

    Because ServiceStack.Text performs much better I recently stumbled across a comparison of JSON seria ...

  3. java 反射技术

    什么是反射?反射就是将字节码中的各种成分映射到相应的java类中来,java反射技术自JDK1.1以来就出现了,目前大多数流行的框架都采用了这种技术,可见其重要性,这篇文章将详细介绍我对java反射技 ...

  4. C Primer Plus(第五版)8

    第 8 章 字符输入/输出和输入确认 在本章中你将学习下列内容: · 有关输入,输出以及缓冲和非缓冲输入之间的区别的更多内容. · 从键盘模拟文件结尾条件的方法. · 如何重定向将你的程序与文件相连接 ...

  5. C++primer 练习15.26

    定义Quote和Bulk_Quote的拷贝控制成员,令其与合成的版本行为一致.为这些成员以及其他构造函数添加打印状态的 语句,使得我们能够知道正在运行哪个程序.使用这些类编写程序,预测程序将创建和销毁 ...

  6. (easy)LeetCode 202.Happy Number

    Write an algorithm to determine if a number is "happy". A happy number is a number defined ...

  7. hadoop配置优化

    yarn-site.xml <property> <name>yarn.nodemanager.resource.memory-mb</name> <valu ...

  8. ORACLE directory 目录--转载

    Create directory让我们可以在Oracle数据库中灵活的对文件进行读写操作,极大的提高了Oracle的易用性和可扩展性.其语法为:CREATE [OR REPLACE] DIRECTOR ...

  9. .NET调用Java写的WebServices(可能会碰到的问题)

    1)net中定义的的WebService(返回值和参数都是自定义对象)可以被Java识别并调用,可是在Java中定义的WebService(返回值和参数都是自定义对象),C#客户端可以识别到自定义对象 ...

  10. C# 位域[flags] 转

    C# 位域[flags] .NET中的枚举我们一般有两种用法,一是表示唯一的元素序列,例如一周里的各天:还有就是用来表示多种复合的状态.这个时候一般需要为枚举加上[Flags]特性标记为位域,例如: ...