惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Latest
Security Latest
Know Your Adversary
Know Your Adversary
S
Schneier on Security
K
Kaspersky official blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google DeepMind News
Google DeepMind News
Project Zero
Project Zero
Recorded Future
Recorded Future
T
The Blog of Author Tim Ferriss
P
Palo Alto Networks Blog
Engineering at Meta
Engineering at Meta
MyScale Blog
MyScale Blog
L
LINUX DO - 热门话题
The Register - Security
The Register - Security
B
Blog
V
Vulnerabilities – Threatpost
M
MIT News - Artificial intelligence
P
Proofpoint News Feed
Cyberwarzone
Cyberwarzone
Latest news
Latest news
Webroot Blog
Webroot Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
CXSECURITY Database RSS Feed - CXSecurity.com
L
LINUX DO - 最新话题
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
B
Blog RSS Feed
aimingoo的专栏
aimingoo的专栏
The GitHub Blog
The GitHub Blog
D
Docker
G
Google Developers Blog
T
Threatpost
F
Full Disclosure
Attack and Defense Labs
Attack and Defense Labs
L
Lohrmann on Cybersecurity
Application and Cybersecurity Blog
Application and Cybersecurity Blog
月光博客
月光博客
D
DataBreaches.Net
WordPress大学
WordPress大学
F
Fortinet All Blogs
S
Secure Thoughts
Y
Y Combinator Blog
博客园 - Franky
Scott Helme
Scott Helme
Apple Machine Learning Research
Apple Machine Learning Research
Hacker News: Ask HN
Hacker News: Ask HN
酷 壳 – CoolShell
酷 壳 – CoolShell
Cloudbric
Cloudbric
N
News | PayPal Newsroom

博客园 - vito qi

安装VS2015出现的bug,各位安装请注意 苹果编程语言Swift简介 设计模式之三职责链模式 HTML页面做中间页跳转传递参数 第一个 ASP.NET Web API应用程序 ASP.NET MVC中 Jquery AJAX 获取数据利用MVC模型绑定实现输出 ASP.NET应用程序与页面生命周期 自定义服务器控件ImageButton 成功项目经理的三种领导力行为 设计模式之二抽象工厂设计模式 ASP.NET 实现文件下载 C#实现根据IP 查找真实地址 IIS 7.0的集成模式和经典模式 ASP.NET 4.0: 请求验证模式变化导致ValidateRequest=false失效 设计模式之—简单工厂设计模式 ASP.NET MVC 学习笔记(一) 数据库分离附加工具 c# 新特性 c#总结(一)
深入理解C#之 参数传递 ref out params
vito qi · 2012-06-25 · via 博客园 - vito qi

在讲C#参数传递之前,我们先简单讨论下 c#中值类型和引用类型的定义以及区别,有助于我们更好的理解参数传递。

我们从内存的角度来简单讨论下值类型和引用类型的区别。我们都知道值类型存储在栈上,引用类型分别在栈和托管堆上。如下图:

我们通过例子来看下 值类型和引用类型存储结构不同有哪些区别:

定义一个类 (引用类型)

View Code

1 public class Student
2     {
3         public int Age { get; set; }
4 
5         public void Say()
6         {
7             Console.WriteLine("我的年龄是:"+Age);
8         }
9     }

定义一个结构(值类型)

View Code

1 public struct StructStudent
2     {
3         public int Age { get; set; }
4 
5         public void Say()
6         {
7             Console.WriteLine("我的年龄是:" + Age);
8         }
9     }

在控制台输出定义如下代码:

View Code

 1  static void Main(string[] args)
 2         {
 3             Student stu1 = new Student { Age=20};
 4             Student stu2 = new Student();
 5             stu2 = stu1;
 6             stu2.Age = 30;
 7             stu1.Say();
 8 
 9             stu2.Say();
10         }

输出结果:

我们将类换成结构,在看看结果如何

定义代码:

View Code

1 static void Main(string[] args)
2         {
3             StructStudent stu1 = new StructStudent { Age = 20 };
4             StructStudent stu2 = new StructStudent();
5             stu2 = stu1;
6             stu2.Age = 30;
7             stu1.Say();
8             stu2.Say();
9         }

输出结果:

我们会发现同样的代码 但是结果不一样,其实 每个变量 都有其堆栈,不同的变量不能共用一个堆栈地址。直接上图

由图 我们可以看出 对于值类型 stu2的age属性更改不会影响 stu1 对于引用类型 stu1 和stu2 在栈上的地址是不同的。stu1=stu2 。这是2个相等的变量 所以他们在托管堆上的引用地址是一样的。修改age 会对stu1 stu2都造成影响。所以 2种情况下输出结果会不一样。

明白这些 我们就很容易理解参数传递了。

关于参数传递 分按值传递 和按 引用传递

按值传递 分 值类型参数按值快递 和 引用类型参数按值传递

按引用传递分 值类型参数按引用传递 和引用类型参数按引用传递

在讲解参数传递之前 我们还要明白一个概念 形参和实参

View Code

 1  class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5 
 6             string str = "da jia hao";
 7             //str 是实际参数
 8             SayHello(str);
 9         }
10         //strMessage是 形式参数
11         public static void SayHello(string strMessage)
12         {
13             Console.WriteLine(strMessage);
14         }
15     }

形参和实参的类型 个数 顺序 必须要完全匹配。

1、值类型参数按值传递:

View Code

 1        static void Main(string[] args)
 2         {
 3             int sum = 10;
 4             Add(sum);
 5             Console.WriteLine(sum);
 6         }
 7 
 8         public static void Add(int i)
 9         {
10             i++;
11         }

输出 结果:

 我们可以看出sum的值 并没有改变还是10. 我们通过图表来说明为什么会这样。我们知道值类型都是存储在栈上。

2、引用类型参数按值传递:

 定义一个引用类型:

View Code

1 public class Student
2     {
3         public int Age { get; set; }
4     }

View Code

 1  static void Main(string[] args)
 2         {
 3             Student stu = new Student { Age =20};
 4             Add(stu);
 5             Console.WriteLine(stu.Age);
 6         }
 7 
 8         public static void Add(Student stu)
 9         {
10             stu.Age++;
11         }

输出结果:

 可以看出结果改变了 输出是21.

为什么会是这样呢,直接上图:

3、值类型按引用传递

View Code

 1       static void Main(string[] args)
 2         {
 3             int sum = 10;
 4             Add(ref sum);
 5             Console.WriteLine(sum);
 6         }
 7 
 8         public static void Add(ref int i)
 9         {
10             i++;
11         }

输出结果:

 可以看出 这次sum的 值改变了 不是 10 而是11. 这就是ref 关键字的神奇作用。ref在参数传递中到底起了什么作用呢,我们可以理解为ref 仅仅是一个地址。在文章前面我们提到过, 每个变量都有其堆栈,不同的变量不能共用一个堆栈地址。所以形参和实参在栈上的地址是不一样的。ref所起的作用我们可以简单的理解为他会记住实参的地址,当形参的值改变后,他会把值映射回 之前的实参地址。

4、引用类型按引用传递

View Code

1  public class Student
2     {
3         public int Age { get; set; }
4     }

View Code

 1 static void Main(string[] args)
 2         {
 3             Student stu = new Student { Age=20};
 4             Add(ref stu);
 5             Console.WriteLine(stu.Age);
 6         }
 7 
 8         public static void Add(ref Student stu)
 9         {
10             stu.Age++;
11         }

在使用ref 的时候 调用方法一定要加上ref关键字 ,Add(int i) 和Add(ref int i)是构成方法的重载的。

在参数传递中 我们都使用ref 作为示例,那么out关键字 和ref 有什么区别呢。ref和out本质基本一样。唯一的区别就是一个是侧重于修改,一个侧重于输出。

static void Main(string[] args)
        {
            int sum = 10;
            Add(ref sum);
            Console.WriteLine(sum);
        }

        public static void Add(ref int i)
        {
            i++;
        }

这段代码中 我们使用ref 之前 先给 sum 赋了初始值,然后修改sum的值。如果我们使用out 可以先不给sum赋值。在方法内部赋值。

 params 关键字 是数组类型的参数。可以指定在参数数目可变处采用参数的方法参数,在方法声明中的 params 关键字之后不允许任何其他参数,并且在方法声明中只允许一个 params 关键字。也就是说方法中如果有多个参数只允许有一个params参数,而且要放在方法的最后一个参数。

参见 MSDN示例:

View Code

 1 // cs_params.cs
 2 using System;
 3 public class MyClass 
 4 {
 5 
 6     public static void UseParams(params int[] list) 
 7     {
 8         for (int i = 0 ; i < list.Length; i++)
 9         {
10             Console.WriteLine(list[i]);
11         }
12         Console.WriteLine();
13     }
14 
15     public static void UseParams2(params object[] list) 
16     {
17         for (int i = 0 ; i < list.Length; i++)
18         {
19             Console.WriteLine(list[i]);
20         }
21         Console.WriteLine();
22     }
23 
24     static void Main() 
25     {
26         UseParams(1, 2, 3);
27         UseParams2(1, 'a', "test"); 
28 
29         // An array of objects can also be passed, as long as
30         // the array type matches the method being called.
31         int[] myarray = new int[3] {10,11,12};
32         UseParams(myarray);
33     }
34 }

string 类型我们都知道是我们最常用的一种引用类型,那么sting类型的参数传递会是什么样的呢,我们可以自己试验一下,会发现string是很特殊的一个引用类型。后续我会继续讲解关于string类型。 不正之处 欢迎指正!