九、传值输出引用数组具名可选参数、扩展方法

九、各种参数和扩展方法

更详细、准确的描述可以看方法参数按值传递。 修饰符启用按引用传递语义,包括只读和 out 参数等区别。 了解不同的参数传递模式以及如何使用它们。 参数修饰符允许一系列可选参数。 - C# | Microsoft Learn

传值参数

在这里插入图片描述

会在内存上创建实际参数的副本。

值类型

在这里插入图片描述

引用类型

在这里插入图片描述

可以通过getHashCode方法,来查看哈希值,因为哈希值具有唯一性,所以可以通过这个判断是否是同一对象实例。

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            Student stu = new Student() {Name = "Tim"};
            SomeMethod(stu);
            //打出来的是Tom
            Console.WriteLine(stu.GetHashCode());
            //此时打出来的是Tim
            //通过哈希值可以看出,两个对象实例并不一样

        }
        //注意此时,两个stu在内存上面的地址是不一样的
        //但是存储着同一样的堆上面同一个对象实例的地址
        static void SomeMethod(Student stu) {
            stu = new Student() { Name = "Tom"};
            Console.WriteLine(stu.GetHashCode());
        }
    }
    class Student {
        public string Name { get; set; }
    }
}

在这里插入图片描述

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            Student stu = new Student() {Name = "Tim"};
            UpdateObject(stu);
            Console.WriteLine(stu.GetHashCode() + "  " + stu.Name);
            /* 
             * 此时会发现,两者的哈希值是一样的 
             * 也就说明两个变量引用的是同一个对象实例
             */
        }
        static void UpdateObject(Student stu) {
            stu.Name = "Tom";//这并不是方法的主要作用,而是副作用
            //side-effect
            Console.WriteLine(stu.GetHashCode() + "  " + stu.Name);
        }
    }
    class Student {
        public string Name { get; set; }
    }
}

这就是传引用类型,和传值类型的区别。

其根本都是因为,引用类型变量引用的堆上面的地址,而值类型的变量代表着的是栈上面数据的地址。

引用参数

在这里插入图片描述

不会在内存上创建实际参数的副本,甚至引用参数的地址直接就是实际参数在栈上面的地址。

引用参数,的主要作用是,打算修改一个变量的值。

值类型

在这里插入图片描述

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            int y = 1;
            IWantSideEffect(ref y);
            /* 
             * 调用的时候必须带上ref
             * 也是为了提醒程序员,这里是引用调用的
             * 这也是C#是强类型语言的表现
             */
            Console.WriteLine(y);
        }
        static void IWantSideEffect(ref int x) {
            x += 100;
        }
    }
}

引用类型

在这里插入图片描述

原本引用的对象实例被替换了

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            Student outterStu = new Student() { Name = "Tim" };
            
            Console.WriteLine(outterStu.GetHashCode() + "  " + outterStu.Name);
            Console.WriteLine("----------------------");
            IWantSideEffect(ref outterStu);
            Console.WriteLine(outterStu.GetHashCode() + "  " + outterStu.Name);
        }
        static void IWantSideEffect(ref Student stu) {
            stu = new Student() {Name = "Tom"};
            
            Console.WriteLine(stu.GetHashCode() + "  " + stu.Name);
        }
    }
    class Student {
        public string Name { get; set; }
    }
}

在这里插入图片描述

两个变量在栈上面的内存都是一样的。

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            Student outterStu = new Student() { Name = "Tim" };
            
            Console.WriteLine(outterStu.GetHashCode() + "  " + outterStu.Name);
            Console.WriteLine("----------------------");
            SomeSideEffect(ref outterStu);
            Console.WriteLine(outterStu.GetHashCode() + "  " + outterStu.Name);
        }
        /* outterStu和方法中的stu
         * 甚至在栈上面都是同一个地址,并且存储了堆上面一个对象实例的地址
         */
        static void SomeSideEffect(ref Student stu) {
            stu.Name = "Tom";
            
            Console.WriteLine(stu.GetHashCode() + "  " + stu.Name);
        }
    }
    class Student {
        public string Name { get; set; }
    }
}

输出参数

在这里插入图片描述

输出参数的作用是,为了向外输出。

所以可以先不赋值

值类型

在这里插入图片描述

使用输出参数的实例:

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            Console.WriteLine("Please input first number:");
            string str1 = Console.ReadLine();
            double x = 0;
            bool b1 = double.TryParse(str1, out x);
            if(b1 == false ) {
                Console.WriteLine("Input error!");
                return;
            }

            Console.WriteLine("Please input second number:");
            string str2 = Console.ReadLine();
            double y = 0;
            bool b2 = double.TryParse(str2, out y);
            if (b2 == false) {
                Console.WriteLine("Input error!");
                return;
            }

            double z = x + y;
            Console.WriteLine($"{x} + {y} = {z}");
        }
    }
}

声明输出参数:

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            Console.WriteLine("Please input first number:");
            string str1 = Console.ReadLine();
            double x = 100;
            bool b1 = double.TryParse(str1, out x);
            if(b1 == true ) {
                Console.WriteLine(x + 1);
            }
        }
    }
    class DoubleParser {//自己实现的带有输出参数的方法
        public static bool TryParse(string str, out double result) {
            try {
                result = double.Parse(str);
                return true;
            } catch {
                result = 0;//实际的double.TryParse()方法,也是这样的
                return false;
            }
        }
    }
}

引用类型

当输出参数是引用类型和是值类型,实际上是差不多的

在这里插入图片描述

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            Student stu = null;
            bool b = StudentFactory.Create("Tom", 20, out stu);
            if(b == true) {
                Console.WriteLine($"Student {stu.Name}, Age {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;

        }
    }
}

数组参数

在这里插入图片描述

只有最后一个参数才能被params修饰。

示例:

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            int result = CalculateSum(1, 2, 3);
            Console.WriteLine(result);

            string str = "Tim;Tom,Amy.Lisa";
            string[] res = str.Split('.', ';', ',');
            foreach (var name in res)
            {
                Console.WriteLine(name);
            }

        }
        /* params修饰符,
         * 即可以传入多个参数,组成的数组
         */
        static int CalculateSum(params int[] intArray) {
            int sum = 0;
            foreach (var item in intArray)
            {
                sum += item;
            }
            return sum;
        }
    }
}

具名参数

调用一个方法时,传进去的参数是带有名字的。

参数的位置,不受约束。

可以看这个命名参数和可选参数 - C# 编程指南 - C# | Microsoft Learn

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            PrintInfo("Tom", 12);//这是不具名调用
            PrintInfo(age : 12, name: "Tom");
            /* 具名调用的优点:
             * 1. 提高代码的可读性
             * 2. 使用具名调用时,参数的位置就不受参数列表的顺序的约束了
             * 3. 具名参数并不是参数的种类,而是参数的使用方法
             */
        }
        static void PrintInfo(string name, int age) {
            Console.WriteLine($"Hello {name}, your are {age}");
        }
    }   
}

可选参数

参数因为声明时具有默认值,而变得可选

不推荐使用可选参数

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            PrintInfo();
        }
        static void PrintInfo(string name = "Tom", int age = 12) {
            Console.WriteLine($"Hello {name}, your are {age}");
        }
    }
}

扩展方法(this参数)

在这里插入图片描述

namespace ParametersExample {
    internal class Program {
        static void Main(string[] args) {
            double x = 3.14159;
            double y = x.Round(4);
            /* 括号里面只写一个参数的原因
             * x就是第一个参数,所以只需要写第二个参数
             * 当无法对一个类型的源码进行修改的时候,
             * 我们可以使用扩展方法,来为这种目标数据类型追加方法
             */
            Console.WriteLine(y);
        }
    }
    /* 创建扩展方法,先创建静态类
     */
    static class DoubleExtension {
        public static double Round(this double input, int digits) { 
            double result = Math.Round(input, digits);
            return result;
        }
    }
}

使用示例:

using System.Linq;

namespace ParametersExample {
    internal class Program {
        /* 当我们想要判断一个数组
         * 是否大于10
         */
        static void Main(string[] args) {
            List<int> myList = new List<int>() {11, 12, 13, 14, 15};
            //bool result = AllGreaterThanTen(myList);
            bool result = myList.All(i => i > 10);
            /* 这一句话就可以代替下面定义的那个方法了
             */
            Console.WriteLine(result);
        }
        static bool AllGreaterThanTen(List<int> intList) {
            foreach (var item in intList)
            {
                if(item <= 10) {
                    return false;
                }
            }
            return true;
        }
    } 
}

总结

  • 传值参数:参数的默认传递方式
  • 输出参数:用于除返回值外还需要输出的场景
  • 引用参数:用于需要修改实际参数值的场景
  • 数组参数:用于简化方法的调用
  • 具名参数:提高可读性
  • 可选参数:让参数拥有默认值
  • 扩展方法(this参数):为目标数据类型“追加”方法
      */
        Console.WriteLine(result);
    }
    static bool AllGreaterThanTen(List<int> intList) {
        foreach (var item in intList)
        {
            if(item <= 10) {
                return false;
            }
        }
        return true;
    }
} 

}






## 总结

> * 传值参数:参数的默认传递方式
> * 输出参数:用于除返回值外还需要输出的场景
> * 引用参数:用于需要修改实际参数值的场景
> * 数组参数:用于简化方法的调用
> * 具名参数:提高可读性
> * 可选参数:让参数拥有默认值
> * 扩展方法(this参数):为目标数据类型“追加”方法



  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值