一、值参数
1.传值参数 → 值类型
虚线以上,方法之外;
虚线以上,方法之内。
值参数会创建变量的副本
对值参数的操作不会影响变量的值
class Program
{
static void Main(string[] args)
{
Student stu = new Student();
int y = 100;
stu.AddOne(y);//101
Console.WriteLine(y);//100
}
}
class Student
{
public void AddOne(int x)//值参数x;int值类型
{
x = x + 1;
Console.WriteLine(x);
}
}
2.传值参数 → 引用类型,并且新创建对象
这种状况很少见,一般情况都是传进来引用它的值,而不是连接到新对象去。
class Program
{
static void Main(string[] args)
{
Student stu = new Student() { Name = "Tim" };
SomeMethod(stu);//Tom
Console.WriteLine(stu.Name);//Tim
}
static void SomeMethod(Student stu)
{
stu = new Student() { Name = "Tom" };
Console.WriteLine(stu.Name);
}
}
class Student
{
public string Name { get; set; }
}
Object.GetHashCode()
方法,用于获取当前对象的哈希代码,每个对象的 HashCode 都不一样。
通过 HashCode 来区分两个 Name 相同的 stu 对象
class Program
{
static void Main(string[] args)
{
Student stu = new Student() { Name = "Tim" };
SomeMethod(stu);//46104728,Tim
Console.WriteLine("{0},{1}", stu.GetHashCode(), stu.Name);//12289376,Tim
}
static void SomeMethod(Student stu)
{
stu = new Student() { Name = "Tim" };
Console.WriteLine("{0},{1}",stu.GetHashCode(),stu.Name);
}
}
class Student
{
public string Name { get; set; }
}
3.传值参数 → 引用类型,只操作对象,不创建新对象
这种通过传递进来的参数修改其引用对象的值的情况,在工作中也比较少见。
因为作为方法,其主要输出还是靠返回值。我们把这种修改参数所引用对象的值的操作叫做方法的副作用(side-effect),这种副作用平时编程时要尽量避免。
class Program
{
static void Main(string[] args)
{
Student stu = new Student() { Name = "Tim" };
UpdateObject(stu);
Console.WriteLine("HashCode={0},Name={1}", stu.GetHashCode(), stu.Name);
}
static void UpdateObject(Student stu)
{
//修改参数所引用着的值的操作,方法的副作用,side-effect
stu.Name = "Tom";
Console.WriteLine("HashCode={0},Name={1}",stu.GetHashCode(),stu.Name);
}
}
class Student
{
public string Name { get; set; }
}
二、引用参数
1.引用参数 → 值类型
引用参数不会为传进来的变量创建副本
引用参数直接指向变量所指向的内存地址
class Program
{
static void Main(string[] args)
{
int y = 1;
IWanSideEffect(ref y);
Console.WriteLine(y);//101
}
static void IWanSideEffect(ref int x)
{
x = x + 100;
}
}
class Student
{
public string Name { get; set; }
}
2.引用参数 → 引用类型,创建新对象
class Program
{
static void Main(string[] args)
{
Student outterStu = new Student() { Name = "Tim" };
Console.WriteLine("HashCode={0},Name={1}", outterStu.GetHashCode(), outterStu.Name);
Console.WriteLine("=================================");
IWanSideEffect(ref outterStu);
Console.WriteLine("HashCode={0},Name={1}", outterStu.GetHashCode(), outterStu.Name);
}
static void IWanSideEffect(ref Student stu)
{
stu = new Student() { Name = "Tom" }; //引用参数赋新值
Console.WriteLine("HashCode={0},Name={1}",stu.GetHashCode(),stu.Name);
}
}
class Student
{
public string Name { get; set; }
}
3.引用参数 → 引用类型,不创建新对象只改变对象值
对象的属性值改变了,但对象的 HashCode 没有改变—操作的是同一个对象
class Program
{
static void Main(string[] args)
{
Student outterStu = new Student() { Name = "Tim" };
Console.WriteLine("HashCode={0},Name={1}", outterStu.GetHashCode(), outterStu.Name);
Console.WriteLine("======================================");
SomeSideEffect(ref outterStu);
Console.WriteLine("HashCode={0},Name={1}", outterStu.GetHashCode(), outterStu.Name);
}
static void SomeSideEffect(ref Student stu)
{
stu.Name = "Tom";
Console.WriteLine("HashCode={0},Name={1}",stu.GetHashCode(),stu.Name);
}
}
class Student
{
public string Name { get; set; }
}
Notes:
引用类型的引用参数(只操作对象不创建新对象) 与 引用类型的传值参数(只操作对象不创建新对象) 结果一样,但内存机理不同。
①传值参数在内存中创建了实际参数的副本,方法里面的 stu参数 和 outterStu变量 不是同一个对象,栈内存地址不一样,但存储的堆内存地址是相同的,存储的是Student实例在堆内存中的地址。
②引用参数在内存中没有创建实际参数的副本,方法里面的 stu参数 和 outterStu变量 是同一个对象,栈内存地址是一样的,存储的是对象在堆内存中的地址。
三、输出形参
1.输出参数 → 值类型
①调用带有输出参数的方法
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please input first number:");
string arg1 = Console.ReadLine();
double x = 0;
bool b1 = double.TryParse(arg1, out x);
if (b1 == false)
{
Console.WriteLine("input error");
return;
}
Console.WriteLine("Please input second number:");
string arg2 = Console.ReadLine();
double y = 0;
bool b2 = double.TryParse(arg2, out y);
if (b2 == false)
{
Console.WriteLine("input error");
return;
}
double z = x + y;
Console.WriteLine("{0}+{1}={2}",x,y,z);
}
}
②声明带有输出参数的方法
class Program
{
static void Main(string[] args)
{
double x = 110;
bool b = DoubleParser.TryParse("abd", out x);
if (b == true)
{
Console.WriteLine(x + 1);
}
else
{
Console.WriteLine(x);
}
}
}
class DoubleParser
{
public static bool TryParse(string input, out double result)
{
try
{
result = double.Parse(input);//在方法体内为输出参数赋值
return true;
}
catch (Exception)
{
result = 0;
return false;
}
}
}
2.输出参数 → 引用类型
class Program
{
static void Main(string[] args)
{
Student stu = null;
bool b = StudentFactory.Create("Tim", 34, out stu);
if (b == true)
{
Console.WriteLine("Student {0},age is {1}", stu.Name, stu.Age);
}
}
}
class Student
{
public int Age { get; set; }
public string Name { get; set; }
}
class StudentFactory
{
public static bool Create(string stuName, int stuAge, out Student result)
{
result = null;
if (string.IsNullOrEmpty(stuName))
{
return false;
}
if (stuAge < 20 && stuAge > 80)
{
return false;
}
result = new Student() { Name = stuName, Age = stuAge };
return true;
}
}
四、数组参数
Notes:
在方法的参数列表当中只能有一个参数是数组参数;而且这个参数必须是参数列表当中的最后一个。
1.使用 params 关键字前
class Program
{
static void Main(string[] args)
{
//声明数组
int[] myintArray = new int[] { 1, 2, 3 };
int result = CalculateSum(myintArray);
Console.WriteLine(result);
}
//计算整型数组中所有元素的和
static int CalculateSum(int[] intArray)
{
int sum = 0;
foreach (var item in intArray)
{
sum += item;
}
return sum;
}
}
2.使用 params 后,不再需要单独声明数组
class Program
{
static void Main(string[] args)
{
////声明数组
//int[] myintArray = new int[] { 1, 2, 3 };
int result = CalculateSum(1,2,3);//不再需要单独声明数组
Console.WriteLine(result);//6
}
//计算整型数组中所有元素的和
static int CalculateSum(params int[] intArray)
{
int sum = 0;
foreach (var item in intArray)
{
sum += item;
}
return sum;
}
}
五、具名参数
1.不具名调用示例
class Program
{
static void Main(string[] args)
{
// 不具名调用
PrintInfo("Tim", 34);//string型的name;int型的age
}
static void PrintInfo(string name, int age)
{
Console.WriteLine("Hello {0},you are {1}.",name,age);
}
}
2.具名调用示例
具名参数:参数位置不受参数列表顺序约束
提高代码可读性
class Program
{
static void Main(string[] args)
{
//具名调用
PrintInfo(age: 34, name: "Tim");//参数位置不受参数列表顺序约束
}
static void PrintInfo(string name, int age)
{
Console.WriteLine("Hello {0},you are {1}.",name,age);
}
}
六、可选参数
参数因为具有默认值而变得“可选”
class Program
{
static void Main(string[] args)
{
//可选参数
PrintInfo();//Hello Tim,you are 34.
}
static void PrintInfo(string name = "Tim", int age = 34)
{
Console.WriteLine("Hello {0},you are {1}.", name, age);
}
}
七、扩展方法(this参数)
1.无扩展方法示例
class Program
{
static void Main(string[] args)
{
double x = 3.14159;
double y = Math.Round(x, 4);//double类型本身没有Round方法,只能使用Math.Round
Console.WriteLine(y);
}
}
2.有扩展方法示例
class Program
{
static void Main(string[] args)
{
double x = 3.14159;
double y = x.Round(4);//调用扩展方法Round
Console.WriteLine(y);
}
}
static class DoubleExtension
{
public static double Round(this double input, int digits)
{
double result = Math.Round(input, digits);
return result;
}
}
3.LINQ方法示例
判断集合中是否所有元素都大于10
①没有使用LINQ扩展方法
class Program
{
static void Main(string[] args)
{
List<int> myList = new List<int>() { 11, 12, 13, 14, 15 };
bool result = AllGreaterThanTen(myList);
Console.WriteLine(result);
}
//判断集合中是否所有元素都大于10
static bool AllGreaterThanTen(List<int> intList)
{
foreach (var item in intList)
{
if (item<=10)
{
return false;
}
}
return true;
}
}
②使用LINQ扩展方法
class Program
{
static void Main(string[] args)
{
List<int> myList = new List<int>() { 11, 12, 13, 14, 15 };
bool result = myList.All(i => i > 10);//使用扩展方法All(),来判断集合中所有元素都大于10
Console.WriteLine(result);
}
}