百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

C#-值传递与引用传递的区别,使用ref与out的区别 086

bigegpt 2025-06-28 12:19 1 浏览

值类型与引用类型

简单来说就是将值类型变量或引用类型变量作为参数传入方法操作后原值的变化

简单回顾,之前细说过值类型与引用类型的区别在内存中的变化,因此不再赘述

1)内存中的区别:值类型主要存储在中;引用类型存储在中栈,堆中(主要是堆)

2)继承中的区别:值类型继承自System.ValueType(System.ValueType又继承自System.Object) 引用类型继承自System.Object;因此C#中Object类可以称之为"祖宗类"

值类型主要有:byte,int,short,float,double,long,decimal,char,bool,struct,enum等

引用类型主要有:object,string,Array,class,interface等

值类型变量赋值会拷贝一个副本 引用类型变量的赋值只复制对对象的引用

static void Main(string[] args)
{
    //值类型
    int n = 6;
    int m = n;
    m = m + 2;
    Console.WriteLine("n = {0}  m = {1}", n, m);
    Console.WriteLine("");
  //修改m的值不会影响n的值
  //验证:拷贝一个副本 
    //引用类型
    int[] ns1 = new int[1] { 1 };
    Console.WriteLine("ns1[0] = {0}", ns1[0]);
    int[] ns2 = ns1;
    Console.WriteLine("ns2[0] = {0}",ns2[0]);
    ns1[0] = 3;
    Console.WriteLine("ns1[0] = {0}",ns1[0]);
   //修改ns2[0]的值会影响ns1[0]的值
  //验证:复制对对象的引用
  //看不懂的单击上面连接或查找039文章
  //详细说明了内存中变量指针的指向,不再赘述
    Console.ReadKey();  
}

值传递

将值类型变量或数据传入方法操作后查看原值(主要是理解值类型传递副本)

 static void Main(string[] args)
 {
     int n = 6;
     M1(n);//在方法内部 X=9
     //方法未返回x操作后的值
    //因此n 还是原值 6
     Console.WriteLine(n);//6
     Console.WriteLine("");
    //在方法内部操作后返回
   //最关键的是n又接收了操作后的值
   //因此 n =9
     n = M2(n);
     Console.WriteLine(n);//9
     //如果 m 没有进行接收
      int m = 6;
      M2(m);
     //M2()虽然有返回值,但是没有接收
     //因此 M2()中 y =9
     //m 还是原值 6
      Console.WriteLine(m);//6
			//m 没有接收M2()的返回值
      //无论在方法内对m做任何操作,外部的m值不变
     Console.ReadKey();
 }
//静态方法
static void M1(int x)
 {
   //无论怎么加减乘除,都不影响外部 n 的值
     x += 3;
 }
 static int M2(int y)
 {
   //操作后返回
   //如果外部没有接收 m还是原值
   //如果外部 m 接收了方法的返回值 
   //m 就是9 相当于 6+3 
     return y += 3;
 }

引用传递

将引用类型的变量或数据传入方法(主要是理解引用类型传递的是对象的地址)

 static void Main(string[] args)
 {
  Person p1 = new Person();
  p1.Name = "张三";
  p1.Age = 18;
  M3(p1);//结果 李四 180
  Console.WriteLine(p1.Name + "  " + p1.Age);    
  Console.ReadKey();
 }
static void M3(Person p3)
{
    p3.Name = "李四";
    p3.Age = 180;
}
 static void Main(string[] args)
 {
  Person p1 = new Person();
  p1.Name = "张三";
  p1.Age = 18;
  M3(p1);//结果 李四 180
  Console.WriteLine(p1.Name + "  " + p1.Age);    
  Console.ReadKey();
 }
static void M3(Person p3)
{
    p3.Name = "李四";
    p3.Age = 180;
}
 static void Main(string[] args)
 {
   Person p3 = new Person();
   p3.Name = "王五";
   p3.Age = 19;
   M6(p3);//王五 19
   Console.WriteLine(p3.Name + "  " + p3.Age);
   Console.ReadKey();
 }
static void M6(Person px)
{
    Person p6 = new Person();
    px = p6;
    px.Name = "张三三";
    px.Age = 190;
}

px与p6指向堆地址 0x002,最后只有p3指向堆地址 0x001

//对照前三个示例,自己理清 M7()方法的结果
static void Main(string[] args)
{
   Person p4 = new Person();
   p4.Name = "王五";
   p4.Age = 19;
   M7(p4);//张三  20
   Console.WriteLine(p4.Name + "  " + p4.Age);  
   Console.ReadKey();
}
 static void M7(Person p4x)
 {
     p4x.Name = "张三";
     p4x.Age = 20;
     Person p7 = new Person();
     p4x = p7;
     p4x.Name = "王五";
     p4x.Age = 190;
 }

使用ref/out进行传值

ref和out都是按地址传递,使用后都将改变原参数的值(值类型,传递的是栈本身的地址;引用类型,传递的是引用地址所在栈的地址)

out修饰的参数在方法外部既可以赋值也可以不赋值;在方法该参数必须赋值

ref修饰的参数与其正好相反

//对照前三个示例,自己理清 M7()方法的结果
static void Main(string[] args)
{
     //使用ref传值
     //传入值类型
     int n = 6;
     M8(ref n);//9
     Console.WriteLine(n);
     Console.WriteLine("");
     //传入引用类型
     int[] ns = new int[] { 1 };
     M9(ref ns);//8
     Console.WriteLine(ns[0]);
     //=========================
     //使用out进行传值
     //传入值类型
     int m = 8;
     M10(out m);//2
     Console.WriteLine(m);
     Console.WriteLine("");
     //传入引用类型
     int[] ms = new int[1];
     ms[0] = 1;
     M11(out ms);//5
     Console.WriteLine(ms[0]);     
     Console.ReadKey();
}
static void M8(ref int nx)
{
    nx = nx + 3;
}
static void M9(ref int[] nsx)
{
    nsx = new int[] { 8 };
}
static void M10(out int m)
{
    m = 2;
}
static void M11(out int[] msx)
{
    msx = new int[1] { 5 };
}

相关推荐

Java 泛型大揭秘:类型参数、通配符与最佳实践

引言在编程世界中,代码的可重用性和可维护性是至关重要的。为了实现这些目标,Java5引入了一种名为泛型(Generics)的强大功能。本文将详细介绍Java泛型的概念、优势和局限性,以及如何在...

K8s 的标签与选择器:流畅运维的秘诀

在Kubernetes的世界里,**标签(Label)和选择器(Selector)**并不是最炫酷的技术,但却是贯穿整个集群管理与运维流程的核心机制。正是它们让复杂的资源调度、查询、自动化运维变得...

哈希Hash算法:原理、应用(哈希算法 知乎)

原作者:Linux教程,原文地址:「链接」什么是哈希算法?哈希算法(HashAlgorithm),又称为散列算法或杂凑算法,是一种将任意长度的数据输入转换为固定长度输出值的数学函数。其输出结果通常被...

C#学习:基于LLM的简历评估程序(c# 简历)

前言在pocketflow的例子中看到了一个基于LLM的简历评估程序的例子,感觉还挺好玩的,为了练习一下C#,我最近使用C#重写了一个。准备不同的简历:image-20250528183949844查...

55顺位,砍41+14+3!季后赛也成得分王,难道他也是一名球星?

雷霆队最不可思议的新星:一个55号秀的疯狂逆袭!你是不是也觉得NBA最底层的55号秀,就只能当饮水机管理员?今年的55号秀阿龙·威金斯恐怕要打破你的认知了!常规赛阶段,这位二轮秀就像开了窍的天才,直接...

5分钟读懂C#字典对象(c# 字典获取值)

什么是字典对象在C#中,使用Dictionary类来管理由键值对组成的集合,这类集合被称为字典。字典最大的特点就是能够根据键来快速查找集合中的值,其键的定义不能重复,具有唯一性,相当于数组索引值,字典...

c#窗体传值(c# 跨窗体传递数据)

在WinForm编程中我们经常需要进行俩个窗体间的传值。下面我给出了两种方法,来实现传值一、在输入数据的界面中定义一个属性,供接受数据的窗体使用1、子窗体usingSystem;usingSyst...

C#入门篇章—委托(c#委托的理解)

C#委托1.委托的定义和使用委托的作用:如果要把方法作为函数来进行传递的话,就要用到委托。委托是一个类型,这个类型可以赋值一个方法的引用。C#的委托通过delegate关键字来声明。声明委托的...

C#.NET in、out、ref详解(c#.net framework)

简介在C#中,in、ref和out是用于修改方法参数传递方式的关键字,它们决定了参数是按值传递还是按引用传递,以及参数是否必须在传递前初始化。基本语义对比修饰符传递方式可读写性必须初始化调用...

C#广义表(广义表headtail)

在C#中,广义表(GeneralizedList)是一种特殊的数据结构,它是线性表的推广。广义表可以包含单个元素(称为原子),也可以包含另一个广义表(称为子表)。以下是一个简单的C#广义表示例代...

「C#.NET 拾遗补漏」04:你必须知道的反射

阅读本文大概需要3分钟。通常,反射用于动态获取对象的类型、属性和方法等信息。今天带你玩转反射,来汇总一下反射的各种常见操作,捡漏看看有没有你不知道的。获取类型的成员Type类的GetMembe...

C#启动外部程序的问题(c#怎么启动)

IT&OT的深度融合是智能制造的基石。本公众号将聚焦于PLC编程与上位机开发。除理论知识外,也会结合我们团队在开发过程中遇到的具体问题介绍一些项目经验。在使用C#开发上位机时,有时会需要启动外部的一些...

全网最狠C#面试拷问:这20道题没答出来,别说你懂.NET!

在竞争激烈的C#开发岗位求职过程中,面试是必经的一道关卡。而一场高质量的面试,不仅能筛选出真正掌握C#和.NET技术精髓的人才,也能让求职者对自身技术水平有更清晰的认知。今天,就为大家精心准备了20道...

C#匿名方法(c#匿名方法与匿名类)

C#中的匿名方法是一种没有名称只有主体的方法,它提供了一种传递代码块作为委托参数的技术。以下是关于C#匿名方法的一些重要特点和用法:特点省略参数列表:使用匿名方法可省略参数列表,这意味着匿名方法...

C# Windows窗体(.Net Framework)知识总结

Windows窗体可大致分为Form窗体和MDI窗体,Form窗体没什么好细说的,知识点总结都在思维导图里面了,下文将围绕MDI窗体来讲述。MDI(MultipleDocumentInterfac...