C#学习(9)传值/输出/引用/数组/具名/可选参数,扩展方法

邮差的信 提交于 2020-02-23 04:45:40

传值参数

声明时不带修饰符的形参是传值参数,一个传值参数对应一个局部变量,只是它的初始值来自该方法调用所提供的相应实参。
在这里插入图片描述

namespace Parameters
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student();
            int y = 100;
            stu.AddOne(y);//101  传进来一个值,把值当作副本,然后只操作这个副本,对原本的值不受影响
            Console.WriteLine(y);//100。 所以y打印结果为100
        }
    
    }

    class Student
    {
        public void AddOne(int x)
        {
            x = x + 1;
            Console.WriteLine(x);
        }
    }
}

在这里插入图片描述

namespace Parameters
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student() { Name="Tim"};
            SomeMethod(stu); //Tom
            Console.WriteLine(stu.Name);//Tim
            
        }

        public static void SomeMethod(Student stu)
        {
            stu = new Student() { Name = "Tom" };//为参数赋了新对象的地址,它不会影响到方法外部的变量
            Console.WriteLine(stu.Name);
        }
    
    }

    class Student
    {
        public string Name { get; set; }
    }
}

在这里插入图片描述

namespace Parameters
{
    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)
        {
            stu.Name = "Tom";
            Console.WriteLine("HashCode={0},Name={1}", stu.GetHashCode(), stu.Name);
        }
    
    }

    class Student
    {
        public string Name { get; set; }
    }
}

运行结果:
在这里插入图片描述

引用参数

引用参数是用ref修饰符声明的形参。与传值参数不同,引用形参并不创建新的存储位置。相反,引用形参表示的存储位置恰是在方法调用中作为实参给出的那个变量所表示的存储位置。
当形参为引用参数时,方法调用中对应的实参必须由关键字ref并后接一个与形参类型相同的variable-reference组成。变量在可以作为引用形参传递之前,必须先明确赋值。

在这里插入图片描述

namespace Parameters
{
    class Program
    {
        static void Main(string[] args)
        {
            int y = 1;
            IWantSideEffect(ref y);
            Console.WriteLine(y);
        }

        static void IWantSideEffect(ref int x)//方法的参数x指向的地址和方法外部的变量y指向同一个内存地址
        {
            x = x + 100;
        }
    
    }

    class Student
    {
        public string Name { get; set; }
    }
}

在这里插入图片描述

namespace Parameters
{
    class Program
    {
        static void Main(string[] args)
        {
            Student outterStu = new Student() { Name = "Tim" };
            Console.WriteLine("HashCode={0},Name={1}", outterStu.GetHashCode(), outterStu.Name);
            IWantSideEffect(ref outterStu);
            Console.WriteLine("HashCode={0},Name={1}", outterStu.GetHashCode(), outterStu.Name);
        }

        static void IWantSideEffect(ref Student stu)//方法的参数stu指向的地址和方法外部的变量outter指向同一个内存地址
        {
            stu = new Student() { Name = "Tom" };
            Console.WriteLine("HashCode={0},Name={1}",stu.GetHashCode(),stu.Name);
        }
    
    }

    class Student
    {
        public string Name { get; set; }
    }
}

运行结果:
在这里插入图片描述
在这里插入图片描述

namespace Parameters
{
    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("*******************************************");
            IWantSideEffect(ref outterStu);
            Console.WriteLine("HashCode={0},Name={1}", outterStu.GetHashCode(), outterStu.Name);
        }

        static void IWantSideEffect(ref Student stu)//方法的参数stu指向的地址和方法外部的变量outter指向同一个内存地址
        {
            stu.Name = "Tom";
            Console.WriteLine("HashCode={0},Name={1}",stu.GetHashCode(),stu.Name);
        }
    
    }

    class Student
    {
        public string Name { get; set; }
    }
}

运行结果:
在这里插入图片描述
总结:
加ref就是对原引用对象进行操作(不会产生副本,能节约内存消耗)
不加ref就是对这个引用对象的副本进行操作(会产生副本,增加了内存消耗)

输出参数

用out修饰符声明的形参是输出形参。类似于引用参数,输出形参不创建新的存储位置。相反,输出形参表示的存储位置恰是在该方法调用中作为实参给出的那个变量所表示的存储位置。
当形参为输出形参,方法调用中的相应实参必须由关键字out并后接一个与形参类型相同的variable-reference组成,变量在可以作为输出形参传递之前不一定需要明确赋值,但是将变量作为输出形参传递的调用之后,该变量被认为是明确赋值。
在方法内部,与局部变量相同,输出形参最初被认为是未赋值的,因而必须在使用它的值之前明确赋值。
在方法返回之前,该方法的每个输出形参都必须明确赋值。

在这里插入图片描述
如何调用一个带有输出参数的方法

namespace Parameters
{
    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(z);
        } 
    }
}

声明一个带有输出参数的方法

namespace Parameters
{
    class Program
    {
        static void Main(string[] args)
        {
            double x = 10;
            bool b1 = DoubleParser.TryParse("789",out x);
            bool b2 = DoubleParser.TryParse("ABC",out x);//若解析失败,x的初值会被清零
            if (b1 == true)
            {
                Console.WriteLine(x + 1);
            }
        } 
    }

    class DoubleParser 
    {
        public static bool TryParse(string input,out double result)
        {
            try
            {
                result = double.Parse(input);
                return true;
            }
            catch 
            {
                result = 0;
                return false;
            }
        }
    }
}

在这里插入图片描述

namespace Parameters
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student();
            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 string Name { get; set; }
        public int Age { 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;
        }
    }
}

数组参数

  • 必须是形参列表中的最后一个,由params修饰
namespace Parameters
{
    class Program
    {
        static void Main(string[] args)
        {
            int result = CalculateSum(1, 2, 3);
            Console.WriteLine(result);
        } 

        static int CalculateSum(params int[] intArray)
        {
            int sum = 0;
            foreach (var item in intArray)
            {
                sum += item;
            }
            return sum;
        }
    }
}		
  • 举例:String.Format方法和String,Split方法
namespace Parameters
{
    class Program
    {
        static void Main(string[] args)
        {
            string str = "Tim;Tom,Amy.Lisa";
            string[] result = str.Split(',', ';', '.');//Split()方法的输入参数为params修饰的char[]
            foreach (var name in result)
            {
                Console.WriteLine(name);
            }
        } 

        static int CalculateSum(params int[] intArray)
        {
            int sum = 0;
            foreach (var item in intArray)
            {
                sum += item;
            }
            return sum;
        }
    }
}

具名参数

  • 参数的位置不再受约束
namespace Parameters
{
    class Program
    {
        static void Main(string[] args)
        {
            PrintInfo("Tim", 34);//不具名参数
            PrintInfo(name: "Tim", age: 34);//具名参数,增加代码可读性,
                                            //name和age可互换位置不影响输出结果
        } 

        static void PrintInfo(string name,int age)
        {
            Console.WriteLine("Hello {0},you are {1}.",name,age);
        }
    }
}

可选参数

  • 参数因为具有默认值而变得“可选”
  • 不推荐使用可选参数
namespace Parameters
{
    class Program
    {
        static void Main(string[] args)
        {
            PrintInfo();//可选参数,不填自动获得默认值
        } 

        static void PrintInfo(string name="Tim",int age = 34) //参数在声明时具有默认值
        {
            Console.WriteLine("Hello {0},you are {1}.",name,age);
        }
    }
}

扩展方法(this参数)

  • 方法必须是共有,静态的,即被public static所修饰
  • 必须是形参列表中的一个,由this修饰
  • 必须由一个静态类(一般类名为SomeTypeExtension)来统一收纳对SomeType类型的扩展方法
namespace Parameters
{
   class Program
   {
       static void Main(string[] args)
       {
           double x = 3.14159;
           double y = x.Round(4);
           Console.WriteLine(y);
       } 
   }

   static class DoubleExtension//声明Double类的扩展方法
   {
       public static double Round (this double input,int digits)//this参数
       {
           double result = Math.Round(input,digits);
           return result;
       }
   }
}

  • 举例:LINQ方法
namespace Parameters
{
    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);//List<>.all方法为扩展方法
            Console.WriteLine(result);//true
        } 
    }
}

各种参数的使用场景总结

  • 传值参数:参数的默认传递方式
  • 输出参数:用于除返回值外还需要输出的场景
  • 引用参数:用于需要修改实际参数值的场景
  • 数组参数:用于简化方法的调用
  • 具名参数:提高可读性
  • 可选参数:参数拥有默认值
  • 扩展方法(this参数):为目标数据类型“追加”方法
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!