• 小改动,大作为——C# 4.0中的微小改动


    1、可选参数和命名实参

        可选参数和命名实参就如同一对好基友,因为它们经常一起使用。

        1.1 可选参数

              可选参数重在“可选”,即在调用方法时,该参数可以明确指定实参,也可以不指定。如下代码所示,下面代码就包含3个参数,一个必备参数和两个可选参数。 

    1 static void TestMethod(int x,int y=10,string name="LearningHard")
    2 {
    3       Console.WriteLine($"x={x} y={y} name={name}");  
    4 }

        在以上代码中,参数x是必选参数,即调用方法必须指定实参,而其他剩余的两个参数可以不用指定实参,当然,显式的指定也是可以的,如果进行显式地指定了实参,则它们的值就被初始化为实参的值。下面几句代码皆是调用上面方法。

    1 static void Main(string[] args)
    2 {
    3     TestMethod(1,2,"Hello");
    4     TestMethod(2,14);
    5     TestMethod(2);
    6     Console.ReadKey();
    7 }

        在使用可选参数时,需要注意一下几个约束条件:

        (1)所有可选参数必须位于必选参数之后。

        (2)可选参数的默认值必须为常量,如数字、常量字符串、null、const成员和枚举成员等。

        (3)参数数组(由params 修饰声明)不能为可选参数。

        (4)用ref或out关键字标识的参数不能被设置为可选参数。

        1.2、命名实参

            当调用带有可选参数的方法时,如果我们省略了一个参数,编译器默认我们省略的是最后一个参数。但是如果我们只想省略第二个参数该怎么办呢?命名实参可以解决这个问题。

    TestMethod(2,name:"Hello");
    TestMethod(x:2,y:20,name:"Hello");

        以上代码就使用了命名实参的方法调用。从代码可以看出,命名实参就是在为实参指定具体的名称,这样编译器将判断参数的名称是否正确,然后将指定的值赋给对应的参数,从而达到只省略第二个参数的目的。

        1.3 COM互操作的福音

             可选参数和命名实参是C#4.0中最简单的两个特性,它们最大的好处是简化了C#与COM组件的互操作。COM(Component Object Model,即组件对象模型)。

    2、泛型的可变性

        在C#2.0中,泛型并不具备可变性,这种可变性是指协变性和逆变性。在面向对象中,继承就蕴含可变性,当方法声明返回的类型为Stream时,可以在实现中返回一个FileStream类型,这里就存在一个隐式的转化:从FileStream类型(子类引用)转换为Stream(父类引用)。引用类型的数组也存在这种从子类引用到父类引用的转化,例如string[]可以转化为object[]。

        2.1 协变性

              协变性指的是泛型类型参数可以从一个派生类隐式地转化为基类。C#4.0引入out关键字来标记泛型参数,以示支持协变性。

    1  List<object> listObject=new List<object>();
    2  List<string> listStrs=new List<string>();
    3  listObject.AddRange(listStrs);
    4  //listStrs.AddRange(listObject);这是错误的

        在以上的代码中,AddRange方法接收的参数类型为IEnumerable<T>,该接口的定义为IEnumerable<out T>,因为其泛型参数有out关键字标识,所以用这个类型的参数T支持协变性,则可以将List<String>转化为IEnumerable<string>(这是被继承的协变性支持的,因为List<T>实现了IEnumerable<T>接口)。又因为类型参数支持协变性,所以可以进一步把IEnumerable<string>转化为IEnumerable<object>类型。这样,代码listobject.AddRange(liststrs)就是正确的,不会出现编译错误。

        2.2 逆变性

              逆变性指的是泛型类型参数可以从一个基类隐式地转化为派生类,C# 4.0引入in关键字来标记泛型参数,以示支持逆变性。下面以.NET类库中的接口public interface IComparer<in T>为例进行演示。

       List<object> listObject=new List<object>();
       List<string> listStrs=new List<string>();
    
       IComparer<object> objComparer = new TestComparer();
       IComparer<string> stringComparer=new TestComparer();
    
       listStrs.Sort(objComparer);
       //listObject.Sort(stringComparer);这是错误的
    
     internal class TestComparer : IComparer<object>
        {
            public int Compare(object x, object y)
            {
                throw new NotImplementedException();
            }
        }

        在以上代码中,listStrs变量的Sort方法应接收IComparer<string>类型的参数,虽然传入的实参为IComparer<object>类型,但因为IComparer< in T>泛型接口支持逆变,所以可将object转化为string类型,于是代码listStrs.Sort(objComparer)也就可以通过编译了。

       2.3 协变和逆变的注意事项

            并不是所有类型都支持泛型类型参数的协变和逆变性,下面总结使用这两个特性时需要注意的地方。

           (1)只有接口和委托才支持协变和逆变(如Func<out TResult>、Action<in T>),类或泛型方法的类型参数都不支持协变和逆变。

           (2)协变和逆变只适用于引用类型,值类型不支持协变和逆变,所以List<int>无法转化为IEnumerable<object>。

           (3)必须显式地用in或out来标记类型参数。

           (4)委托的可变性不要在多播委托中使用。

        很多人可能会问关于第4点的问题,下面用代码演示一下:

        

    1             Func<string> stringFunc = () => "";
    2             Func<Object> objectFunc=()=>new object();
    3             Func<object> combined = stringFunc + objectFunc;

        这段代码在运行的时候会出现问题的,异常信息为:委托必须具有相同的类型。

        因为Delegate.Combine方法要求参数必须为相同类型。所以上面的代码如果要正确运行,需要进行一下修改:

    1             Func<string> stringFunc = () => "";
    2             Func<Object> objectFunc=()=>new object();
    3 
    4             Func<object> objectFunc2=new Func<object>(stringFunc);
    5             Func<object> combined = objectFunc2 + objectFunc;

        强制转换之后,就可以调用Delegate.Combine方法时确定要创建的委托类型了,从而解决了运行时抛出异常的问题。

  • 相关阅读:
    [LeetCode] 56. Merge Intervals 解题思路
    [LeetCode] 128. Longest Consecutive Sequence 解题思路
    [LeetCode] Subsets I (78) & II (90) 解题思路,即全组合算法
    linux安装PHP7以及扩展
    php安装composer
    细说PHP中strlen和mb_strlen的区别
    mysql一些简单操作
    mysql数据库使用Navicat时向Navicat导入sql文件时某字段过大时的处理
    JS中||的某些用法
    PHP验证身份信息
  • 原文地址:https://www.cnblogs.com/Helius/p/5789293.html
Copyright © 2020-2023  润新知