c# 之传值参数,输出参数,引用参数,数组参数,具名参数,可选参数,扩展方法
1.值参数
声明时不带修饰符的形参是值参数,一个值形参对应于一个局部变量,只是它的初始值来自该方法调用所提供的的相应实参。
定义函数时的参数为形参
pulbic void test(int a, int b)
{
Debug.Log("形参");
}
调用函数时传递的参数为实参
void Start()
{
int a = 1;
int b = 2;
//这里为实参
test(a,b);
}
(1) 值类型的传值参数,如下图,值类型的值参数会创建一个变量的副本,在方法体中改的只是副本的值,不会影响方法体外的变量的值。
实例:
namespace TestClass { class Program { static void Main(string[] args) { Monkey monkey = new Monkey(); int x = 100; monkey.Add(x); Console.WriteLine(x); Console.ReadKey(); } } class Monkey { public void Add(int x) { x++; Console.WriteLine(x); } } }
结果:
101
100
(2) 引用类型的传值参数
引用类型的值参数,在接受变量的时候也会创建一个副本,这个副本中的值存储的就是这个值的地址。
如上图,如果在接收参数之后创建一个新实例,那么就会创建一个新值,在方法体中修改值不会对方法体外的数据造成影响。
如下:
class Program { static void Main(string[] args) { Monkey monkey = new Monkey(); monkey.Name = "2"; Test(monkey); Console.WriteLine(monkey.Name); Console.ReadKey(); } static void Test(Monkey monkey) { monkey = new Monkey(); monkey.Name = "1"; Console.WriteLine(monkey.Name); } }
结果:
1
2
但是如果不创建新的对象,那么更改方法体内的值的同时也会更改方法体外的。
代码如下:
class Program { static void Main(string[] args) { Monkey monkey = new Monkey(); monkey.Name = "2"; Test(monkey); Console.WriteLine(monkey.Name); Console.ReadKey(); } static void Test(Monkey monkey) { monkey.Name = "1"; Console.WriteLine(monkey.Name); } }
结果:
1
1
2.引用参数
引用参数是ref修饰符声明的形参。引用形参并不创建新的存储位置,相反,引用形参表示的存储位置正是在方法调用中作为实参给出的那个变量所表示的存储位置。必须提前声明
(1)值类型的引用参数
示例:
static void Main(string[] args) { int x = 10; Add(ref x); Console.WriteLine(x); Console.ReadKey(); } public static void Add(ref int x) { x++; }
结果:11
(2)引用类性的引用参数
如上图所示,引用类型使用引用参数之后,不会创建副本,引用参数存储的是实参中的地址,所以方法体内的值更改,方法体之外也会随之更改。
例子如下:
// class Program { static void Main(string[] args) { Monkey monkey = new Monkey() { Name = "托尔斯泰" }; Console.WriteLine($"HashCode is {monkey.GetHashCode()} ,name={monkey.Name}"); Console.WriteLine("----------------------------------"); Test(ref monkey); Console.WriteLine($"HashCode is {monkey.GetHashCode()} ,name={monkey.Name}"); Console.ReadKey(); } static void Test(ref Monkey monkey) { monkey = new Monkey() {Name="鲁迅" }; Console.WriteLine($"HashCode is {monkey.GetHashCode()} ,name={monkey.Name}"); } } public class Monkey { public string Name { get; set; } }
结果:
HashCode is 58225482 ,name=托尔斯泰
----------------------------------
HashCode is 54267293 ,name=鲁迅
HashCode is 54267293 ,name=鲁迅
但是有时候方法内部只是更改某个字段,但是不会创建一个新对象,这时候的地址一样的
示例:
namespace TestClass { // class Program { static void Main(string[] args) { Monkey monkey = new Monkey() { Name = "托尔斯泰" }; Console.WriteLine($"HashCode is {monkey.GetHashCode()} ,name={monkey.Name}"); Console.WriteLine("----------------------------------"); Test(ref monkey); Console.WriteLine($"HashCode is {monkey.GetHashCode()} ,name={monkey.Name}"); Console.ReadKey(); } static void Test(ref Monkey monkey) { monkey.Name = "鲁迅"; Console.WriteLine($"HashCode is {monkey.GetHashCode()} ,name={monkey.Name}"); } } public class Monkey { public string Name { get; set; } } }
结果:
HashCode is 58225482 ,name=托尔斯泰
----------------------------------
HashCode is 58225482 ,name=鲁迅
HashCode is 58225482 ,name=鲁迅
一定要注意的是,如果我们更改一下上面的代码:
Test(money);
static void Test(Monkey monkey);
此时就不是引用参数了,而是引用类性的值参数,此时会创建一个变量副本,但是加上ref则不会。
3.输出参数
定义:用out修饰符修饰的形参是输出参数。输出形参不创建新的存储位置。相反,输出形参表示的存储位置恰好是在该方法调用中作为实参给出的那个变量所表示的存储位置。变量在作为输出参数的时候可以不必提前赋值,因为在传入之后之前赋的值会被丢弃。在方法返回之前,该方法的每个输出参数必须有明确的的赋值。
输出参数不创建副本,所以用虚线表示。
4.数组参数
由params修饰,但是需要注意的是必须放在参数列表的最后一个。参数数组就是在函数的数组参数前面加上params,当调用函数传递数组时不需要再new一个,只需要传递任意个跟数组参数类型相同的数就可以
示例:
static void Main(string[] args) { Test(1,2); Console.ReadKey(); } static void Test(params int[] vs) { }
5.具名参数
优点:
(1)提高代码的可读性
(2)不受参数的位置影响,就是不必一定要对应方法上参数的位置。
class Program { static void Main(string[] args) { Test(id:1,name:"test"); Console.ReadKey(); } static void Test(string name,int id) { } }
6.可选参数
直接上例子:
class Program { static void Main(string[] args) { Test(); Console.ReadKey(); } static void Test(string name="test",int id=1) { } }
7.扩展方法(this参数)
(1)方法必须是公有的,静态的,即被public static修饰
(2)必须是参数列表中的第一个。由this修饰
(3)必须在一个静态类中,
(4)如果扩展的类型是SomeType,那么类型应该为SomeTypeExtention
先看下面代码,double类型没有四舍五入的方法,必须借助Math来实现:
static void Main(string[] args) { double x = 3.121251; double y = Math.Round(x,4);//保留4位 Console.WriteLine(y); Console.ReadKey(); }
现在我们通过扩展方法实现让double也有Round这个方法,
class Program { static void Main(string[] args) { double x = 3.121251; double y = x.Round(4);//包留4位 Console.WriteLine(y); Console.ReadKey(); } } public static class DoubleExtention { public static double Round(this double input,int digits) { return Math.Round(input,digits); } }
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术