0

0

详解可选参数和命名参数实例

零下一度

零下一度

发布时间:2017-06-24 09:46:31

|

2820人浏览过

|

来源于php中文网

原创

9.1 可选参数和命名参数

    class Program
    {
        private static int s_n = 0;

        private static void M(int x = 9, string s = "A", DateTime dt = default(DateTime), Guid guid = new Guid())
        {
            Console.WriteLine("x={0},s={1},dt={2},guid={3}", x, s, dt, guid);
        }
        public static void Main()
        {
            //1.等同于M(9,"A",default(DateTime),new Guid());
            M();

            //2.等同于M(8,"X",default(DateTime),new Guid());
            M(8, "X");

            //3.等同于M(5,"A",DateTime.Now,Guid.NewGuid());
            M(5, guid: Guid.NewGuid(), dt: DateTime.Now);

            //4.等同于M(0,"1",default(DateTime),new Guid());
            M(s_n++, s_n++.ToString());

            //5.等同于以下两行代码:
            //string t1="2";int t2=3;
            //M(t2,t1,default(DateTime),new Guid());
            M(s: (s_n++).ToString(), x: s_n++);
        }
    }

9.1.1 规则和原则

  • 可为方法、构造器方法和有参属性(c#索引器)的参数指定默认值。还可为属于委托定义一部分的参数指定默认值。

  • 有默认值的参数必须放在没有默认值的所有参数之后。但有一个例外:“参数数组”这种参数必须放在所有参数(包括有默认值的这些)之后,而且数组本身不能有一个默认值。

  • 默认值必须是编译时能确定的常量值(包括基元类型、枚举类型,以及能设为null的任何引用类型)。值类型的参数可将默认值设为值类型的实例,并让它的所有字段都包含零值。可用defaultnew关键字来表达这个意思,两种语法生成的IL代码完全一致。

  • 不要重命名参数变量,否则任何调用者以传参数名的方式传递实参,它们的代码也必须修改。

  • 如果方法从模块外部调用,更改参数的默认值具有潜在的危险性。call site(发出调用的地方)在它的调用中嵌入默认值。如果以后更改了参数的默认值,但没有重新编译包含call site的代码,它在调用你的方法时就会传递旧的默认值。可考虑将默认值 0/null 作为哨兵值使用,从而指出默认行为。例子:

        //不要这样做:
        private static string MakePath(string filename = "Untitled")
        {
            return string.Format(@"c\{0}.txt", filename);
        }
    
        //而要这样做:
        private static string MakePath(string filename = null)
        {
            return string.Format(@"C:\{0}.txt", filename ?? "Untitled");
        }
  • 如果参数用 ref 或 out 关键字进行了标识,就不能设置默认值。

使用可选或命名参数调用方法时,还要注意以下附加的规则和原则:

  • 实参可按任意顺序传递,但命名实参只能出现在实参列表的尾部

  • 可按名称将实参传给没有默认值的参数,但所有必须的实参都必须传递(无论按位置还是按名称)。

  • C#不允许省略逗号之间的实参,比如 M(1,,DateTime.Now)。对于有默认值的参数,如果想省略它们的实参,以传参数名的方式传递实参即可。

  • 如果参数要求 ref/out ,为了以传参数名的方式传递实参,请使用下面这样的语法:

        //方法声明:
        private static void M(ref int x) { ...}
    
        //方法调用:
        int a = 5;
        M(x: ref a);
  • C#调用COM组件时,如果是以传引用的方式传递实参,C#还允许省略 ref/out ,进一步简化编码。但如果调用的不是COM组件,C#就要求必须向实参应用 ref/out 关键字

9.1.2 DefaultParameterValueAttribute 和 OptionalAttribute

9.2 隐式类型的局部变量

  • 不能用 var 声明方法的参数类型。

  • 不能用 var 声明类型中的字段。

  • 不要混淆 dynamicvar 。用 var 声明局部变量只是一种简化语法,它要求编译器根据表达式推断具体数据类型。 var 关键字只能声明方法内部的局部变量,而 dynamic 关键字适用于局部变量、字段和参数。表达式不能转型为 var ,但能转型为 dynamic 。必须显式初始化用 var 声明的变量,但无需初始化用 dynamic 声明的变量。

        private static void ImplicitlyTypedLocalVariables()
        {
            var name = "Jeff";
            ShowVariableType(name); //显示:System.String

            //var n=null;           //错误,不能将null赋给隐式类型的局部变量
            var x = (String)null;   //可以这样写,但意义不大
            ShowVariableType(x);    //显示:System.String

            var numbers = new int[] { 1, 2, 3, 4 };
            ShowVariableType(numbers);  //显示:System.Int32[]

            //复杂类型能少打一些字
            var collection = new Dictionary() { { "Grant", 4.0f } };

            //显示:System.Collections.Generic.Dictionary`2[System.String,System.Single]
            ShowVariableType(collection);

            foreach (var item in collection)
            {
                //显示:System.Collections.Generic.KeyValuePair`2[System.String,System.Single]
                ShowVariableType(item);
            }
        }

        private static void ShowVariableType(T t)
        {
            Console.WriteLine(typeof(T));
        }

9.3 以传引用的方式向方法传递参数

  • CLR 默认所有方法参数都传值。

  • CLR 允许以传引用而非传值的方式传递参数。C# 用关键字 outref 支持这个功能。

    PHP与MySQL程序设计3
    PHP与MySQL程序设计3

    本书是全面讲述PHP与MySQL的经典之作,书中不但全面介绍了两种技术的核心特性,还讲解了如何高效地结合这两种技术构建健壮的数据驱动的应用程序。本书涵盖了两种技术新版本中出现的最新特性,书中大量实际的示例和深入的分析均来自于作者在这方面多年的专业经验,可用于解决开发者在实际中所面临的各种挑战。 本书内容全面深入,适合各层次PHP和MySQL开发人员阅读,既是优秀的学习教程,也可用作参考手册。

    下载
  • CLR 不区分 outref,无论用哪个关键字,都会生成相同的 IL 代码。另外,元数据也几乎完全一致,只有一个bit除外,它用于记录声明方法时指定的是 out 还是 ref

  • C#编译器是将这两个关键字区别对待的,而且这个区别决定了由哪个方法负责初始化所引用的对象。

  • 使用 out 标记参数的方法,不能读取该参数的值,而且在返回前必须向这个值写入。相反,如果用 ref 标记方法,必须在调用方法前初始化参数的值,被调用的方法可以读取值以及、或者向值写入

  • 使用 out

        public static void Main()
        {
            int x;                  //x没有初始化
            GetVal(out x);          //x不必初始化
            Console.WriteLine(x);   //显示“10”
        }
    
        private static void GetVal(out int v)
        {
            v = 10; //该方法必须初始化v
        }
  • 使用 ref

        public static void Main()
        {
            int x = 5;              //x已经初始化
            GetVal(ref x);          //x必须初始化
            Console.WriteLine(x);   //显示“15”
        }
    
        private static void GetVal(ref int v)
        {
            v += 10; //该方法可使用v的已初始化的值
        }
  • 不能定义仅在 ref 和 out 上有差别的重载方法。

  • 以传引用的方式传给方法的变量,它的类型必须与方法签名中声明的类型相同。

        public static void Main()
        {
            string s1 = "Jeffrey";
            string s2 = "Richter";
    
            //错误!错误!错误!
            //Swap(ref s1, ref s2);   
    
            //以传引用的方式传递的变量,
            //必须和方法预期的匹配
            object o1 = s1, o2 = s2;
            Swap(ref o1,ref o2);
    
            //完事后再将object转型为string
            s1 = (string)o1;
            s2 = (string)o2;
    
            Console.WriteLine(s1);   //显示“Richter”
            Console.WriteLine(s2);   //显示“Jeffrey”
        }
    
        private static void Swap(ref object a, ref object b)
        {
            object t = b;
            b = a;
            a = t;
        }
  • 可用泛型来修正上面方法

        public static void Main()
        {
            string s1 = "Jeffrey";
            string s2 = "Richter";
    
            Swap(ref s1, ref s2);
            Console.WriteLine(s1);   //显示“Richter”
            Console.WriteLine(s2);   //显示“Jeffrey”
        }
    
        private static void Swap(ref T a, ref T b)
        {
            T t = b;
            b = a;
            a = t;
        }

9.4 向方法传递可变数量的参数

  • params 只能应用于方法签名中的最后一个参数。

  • 这个参数只能标识一维数组(任意类型)。

  • 可为这个参数传递 null 值,或传递对包含零个元素的一个数组的引用。

  • 调用参数数量可变的方法对性能有所影响(除非显式传递null)。要减少对性能的影响,可考虑定义几个没有使用 params 关键字的重载版本,如System.String类的Concat方法。

        public static void Main()
        {
            Console.WriteLine(Add(new int[] { 1, 2, 3, 4, 5 }));//显示“15”
            //或
            Console.WriteLine(Add(1, 2, 3, 4, 5));              //显示“15”

            //以下两行都显示“0”
            Console.WriteLine(Add());       //向Add传递 new int[0]
            Console.WriteLine(Add(null));   //向Add传递 null :更高效(因为不会分配数组)
        }

        private static int Add(params int[] values)
        {
            // 注意:如果愿意,可将values数组传给其他方法

            int sum = 0;
            if (values != null)
            {
                for (int x = 0; x < values.Length; x++)
                    sum += values[x];
            }
            return sum;
        }

9.5 参数和返回类型的设计规范

  • 声明方法的参数类型时,应尽量指定最弱的类型,宁愿要接口也不要基类。例如,如果要写方法来处理一组数据项,最好是用接口(比如 IEnumerable)声明参数,而不要用强数据类型(比如List)或者更强的接口类型(比如ICollectionIList):

        //好:方法使用弱参数类型
        public void ManipulateItems(IEnumerable collection){}
    
        //不好:方法使用强参数类型
        public void ManipulateItems(List collection) { }
  • 相反,一般最好是将方法的返回类型声明为最强的类型(防止受限于特定类型)。例如,方法最好返回FileStream而不是Stream对象:

        //好:方法使用强返回类
        public FileStream OpenFile() { }
    
        //不好:方法使用弱返回类
        public Stream OpenFile() { }
  • 如果想保持一定的灵活性,在将来更改方法返回的东西,请选择一个较弱的返回类型。

        //灵活:方法使用较弱的返回类型
        public IList GetStringCollection() { }
    
        //不灵活:方法使用较强的返回类型
        public List GetStringCollection() { }

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Golang处理数据库错误教程合集
Golang处理数据库错误教程合集

本专题整合了Golang数据库错误处理方法、技巧、管理策略相关内容,阅读专题下面的文章了解更多详细内容。

132

2026.02.06

java多线程方法汇总
java多线程方法汇总

本专题整合了java多线程面试题、实现函数、执行并发相关内容,阅读专题下面的文章了解更多详细内容。

52

2026.02.06

1688阿里巴巴货源平台入口与批发采购指南
1688阿里巴巴货源平台入口与批发采购指南

本专题整理了1688阿里巴巴批发进货平台的最新入口地址与在线采购指南,帮助用户快速找到官方网站入口,了解如何进行批发采购、货源选择以及厂家直销等功能,提升采购效率与平台使用体验。

748

2026.02.06

快手网页版入口与电脑端使用指南 快手官方短视频观看入口
快手网页版入口与电脑端使用指南 快手官方短视频观看入口

本专题汇总了快手网页版的最新入口地址和电脑版使用方法,详细提供快手官网直接访问链接、网页端操作教程,以及如何无需下载安装直接观看短视频的方式,帮助用户轻松浏览和观看快手短视频内容。

442

2026.02.06

C# 多线程与异步编程
C# 多线程与异步编程

本专题深入讲解 C# 中多线程与异步编程的核心概念与实战技巧,包括线程池管理、Task 类的使用、async/await 异步编程模式、并发控制与线程同步、死锁与竞态条件的解决方案。通过实际项目,帮助开发者掌握 如何在 C# 中构建高并发、低延迟的异步系统,提升应用性能和响应速度。

48

2026.02.06

Python 微服务架构与 FastAPI 框架
Python 微服务架构与 FastAPI 框架

本专题系统讲解 Python 微服务架构设计与 FastAPI 框架应用,涵盖 FastAPI 的快速开发、路由与依赖注入、数据模型验证、API 文档自动生成、OAuth2 与 JWT 身份验证、异步支持、部署与扩展等。通过实际案例,帮助学习者掌握 使用 FastAPI 构建高效、可扩展的微服务应用,提高服务响应速度与系统可维护性。

51

2026.02.06

JavaScript 异步编程与事件驱动架构
JavaScript 异步编程与事件驱动架构

本专题深入讲解 JavaScript 异步编程与事件驱动架构,涵盖 Promise、async/await、事件循环机制、回调函数、任务队列与微任务队列、以及如何设计高效的异步应用架构。通过多个实际示例,帮助开发者掌握 如何处理复杂异步操作,并利用事件驱动设计模式构建高效、响应式应用。

37

2026.02.06

java连接字符串方法汇总
java连接字符串方法汇总

本专题整合了java连接字符串教程合集,阅读专题下面的文章了解更多详细操作。

91

2026.02.05

java中fail含义
java中fail含义

本专题整合了java中fail的含义、作用相关内容,阅读专题下面的文章了解更多详细内容。

38

2026.02.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 9万人学习

python编程入门系列图文教程
python编程入门系列图文教程

共65课时 | 24.8万人学习

vscode其实很简单
vscode其实很简单

共72课时 | 29.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号