• 谈谈C# 4.0新特性“缺省参数”的实现


    谈谈C# 4.0新特性“缺省参数”的实现

    http://www.cnblogs.com/artech/archive/2011/01/12/optionalparameter.html

     

    C#4.0关于缺省参数的新特性,相信大家都不会陌生。所谓缺省参数,顾名思义,就是在声明方法的某个参数的时候为之指定一个默认值,在调用该方法的时候如果采用该默认值,你就无须指定该参数。和很多语言层面特性(语法糖)的实现一样,缺省参数也是编译器为我们玩的一个小花招。缺省参数最终体现为两个特殊的自定义特性OptionalAttribute和DefaultParameterValueAttribute 。

    目录      一、缺省参数的用法       二、实现缺省参数的两个特性:OptionalAttribute和DefaultParameterValueAttribute       三、直接通过OptionalAttribute和DefaultParameterValueAttribute 定义缺省参数

    一、缺省参数的用法

    比如下面一个TestMethod方法,后面两个参数bar和baz就是缺省参数,默认值分别为“Bar”和“Baz”。

       1: static void TestMethod(string foo, string bar = "Bar", string baz = "Baz")
       2: {
       3:     Console.WriteLine("{0, -5} - {1, -5} - {2, -5}", foo, bar, baz);
       4: } 

    在调用TestMethod的时候,我们自由地选择采用缺省的参数值,或者覆盖该缺省值。

       1: static void Main(string[] args)
       2: {
       3:     TestMethod("Foo");
       4:     TestMethod("Foo", "Bar1");
       5:     TestMethod("Foo", "Bar1", "Baz1");
       6: }

    下面是输出结果:

       1: Foo   - Bar   - Baz
       2: Foo   - Bar1  - Baz
       3: Foo   - Bar1  - Baz1

    缺省参数的使用有两个简单的限制,其一是:缺省参数的声明只能放在普通参数之后。如下代码中定义的TestMethod方法中,缺省参数bar后面跟一个非缺省参数baz,这样的代码是不能通过编译的(编译错误信息为:Optional parameters must appear after all required parameters)。

       1: static void TestMethod(string foo, string bar = "Bar", string baz)
       2: {
       3:     Console.WriteLine("{0, -5} - {1, -5} - {2, -5}", foo, bar, baz);
       4: }   

    但是,缺省参数后面可以跟数组参数(params参数),实际上无论在什么情况下,params参数都只能是最后一个声明的参数。关于缺省参数的声明的位置限制,主要重载方法的识别机制决定的,这一点大家都很容易理解。

    缺省参数的另一个限制是:指定的缺省值必须是一个常量,这就实际上为作为缺省参数的数据类型作了限制——只能是系统定义的基元类型。下面定义的TestMethod方法中,我们定义了一个DateTime类型的缺省参数,并将参数缺省值作为DateTime.Now。由于DateTime.Now不是常量,所以这样的代码也不能通过编译(编译错误消息:Default parameter value for 'date' must be a compile-time constant)。

       1: static void TestMethod(DateTime date = DateTime.Now)
       2: {
       3:    //Others...
       4: }    

    二、实现缺省参数的两个特性:OptionalAttribute和DefaultParameterValueAttribute

    为什么缺省参数的默认值只能接受常量呢?如果你了解了缺省参数的本质,这就不是一个问题。那么缺省参数究竟是如何实现的呢?

    和很多语言层面特性(语法糖)的实现一样,缺省参数也是编译器为我们玩的一个小花招,而真正编译后的东西都是我们再熟悉不过的玩意儿。当包含缺省参数的C#代码经过编译后,缺省参数体现在两个特殊的自定义特性OptionalAttribute和DefaultParameterValueAttribute 。前者将参数标识为缺省参数,后者指定其缺省值。

       1: [ComVisible(true), AttributeUsage(AttributeTargets.Parameter, Inherited=false)]
       2: public sealed class OptionalAttribute : Attribute
       3: {
       4: }
       5:  
       6: [AttributeUsage(AttributeTargets.Parameter)]
       7: public sealed class DefaultParameterValueAttribute : Attribute
       8: {
       9:     public DefaultParameterValueAttribute(object value);
      10:     public object Value {get; }
      11: }

    对于最开始我们定义的TestMethod方法,编译后的形式如下所示。

       1: private static void TestMethod(string foo, 
       2:     [Optional, DefaultParameterValue("Bar")] string bar, 
       3:     [Optional, DefaultParameterValue("Baz")] string baz)
       4: {
       5:     //Others..
       6: }

    正是因为缺省参数的默认值最终是作为DefaultParameterValueAttribute的参数存在的,所以它必须是常量。

    三、直接通过OptionalAttribute和DefaultParameterValueAttribute 定义缺省参数

    既然缺省参数最终体现为OptionalAttribute和DefaultParameterValueAttribute 这两个特性,我们是否可以直接通过它们来定义缺省参数呢?答案是:当然可以,下面的代码一样可以正常执行。

       1: static void Main(string[] args)
       2: {
       3:     TestMethod("Foo");
       4:     TestMethod("Foo","Bar1");
       5:     TestMethod("Foo","Bar1","Baz1");
       6: }
       7:  
       8: private static void TestMethod(string foo, 
       9:     [Optional, DefaultParameterValue("Bar")] string bar, 
      10:     [Optional, DefaultParameterValue("Baz")] string baz)
      11: {
      12:     //Others..
      13: }

    如果调用含有缺省参数的方法,并且没有显示指定该参数,编译器在编译的时候会自动将默认值附加上去。对于上面的Main方法,下面是与之等效的编译后代码。

       1: private static void Main(string[] args)
       2: {
       3:     TestMethod("Foo", "Bar", "Baz");
       4:     TestMethod("Foo", "Bar1", "Baz");
       5:     TestMethod("Foo", "Bar1", "Baz1");
       6: }

    虽然说我们通过OptionalAttribute和DefaultParameterValueAttribute 这两个特性也可以定义缺省参数,但是当我们将缺省参数定义在普通参数之前是,编译器不会报错。倒是方法中缺省参数实际上就相当于普通参数了。

       1: static void Main(string[] args)
       2: {
       3:     //TestMethod("Foo","Baz");
       4:     //上面的方法调用无效
       5:     TestMethod("Foo","Bar1","Baz1");
       6: }
       7: private static void TestMethod(string foo, 
       8:     [Optional, DefaultParameterValue("Bar")] string bar, 
       9:     string baz)
      10: {
      11:     //Others..
  • 相关阅读:
    Tornado-Lesson06-ORM、SQLAlchemy连接数据库、Module和增删改查
    Tornado-Lesson04-模版、模版转义、静态文件的引用
    Tornado-Lesson05-模版继承、函数和类导入、ui_methods和ui_modules
    博弈论
    给图片添加水印
    Apsara Clouder专项技能认证:实现调用API接口
    好用的工具
    书写是为了更好的思考
    使用与破解ExcelVBA密码
    一款可以直接下载浏览器sources资源的Chrome插件
  • 原文地址:https://www.cnblogs.com/suizhikuo/p/2281823.html
Copyright © 2020-2023  润新知