• 学习笔记2_命名和可选参数_协变与逆变


    命名和可选参数

    在开发项目过程中,有时候需要编写一个接收多个参数的方法.例如查询某个销售记录情况,查询条件为销售日期,商品类别,商品编号,销售城市的任意组合.如果指定了某个条件,则根据这个条件进行过滤,否则不对此条件

    进行限制.方法如下

    static List<string> Select(DateTime from, DateTime to, string categoryId, string productId, string cityId)
    {
        return null;
    }

    如果要调用此方法,必须为所有的参数指定一个值,即使不根据这个条件进行查询

    var list = Select(DateTime.Parse("2010-1-1"), DateTime.Parse("2012-1-1"), null, null, "1001");

    在C#4.0中,允许为方法的参数指定默认值,则此参数成为可选参数,调用方法的时候不必给这个参数传值.C#4.0还支持命名参数,调用方法的时候,调用方法可以通过参数名称来为某个特定的参数传值.

    注意:可选参数必须位于方法列表参数的最后.即在可选参数后面不允许出现不可选参数.

    利用C#4.0的可选参数,上述代码可以修改为:

    //可选参数必须位于方法列表的最后
    static List<string> NewSelect(DateTime from, DateTime to, string categoryId = null, string productId = null, string cityId = null)
    {
        return null;
    }

    上面的查询可以修改为:

    var list2 = NewSelect(DateTime.Parse("2010-1-4"), DateTime.Parse("2012-1-1"));

    如果要查询某个城市的销售情况,可以使用

    var list3 = NewSelect(DateTime.Parse("2010-1-4"), DateTime.Parse("2012-1-1"), cityId: "1001");

    协变和逆变

    在C#4.0中引入了协变(Covariance)和逆变(Contravariance),以增强泛型接口和委托.

    在C#中,将一个IList<string>类型转换IList<object>是不允许的.下面代码编译时会出错

    IList<string> words = new List<string> { "this", "is", "a", "array" };
    IList<object> objects = words;

    "无法将类型IList<string>隐式转换为IList<object>".C#之所以不允许将IList<string>转换为IList<object>是出于安全的原因.如果允许,则考虑如下代码:

    IList<string> words = new List<string> { "this", "is", "a", "array" };
    //假定可以编译
    IList<object> objects = words;
    objects[0] = 50;
    objects[1] = 123.456;
    objects[2] = new Class1();
    string s = objects[2];

    假如可以将IList<string>转换为IList<object>,由于IList是引用类型,objects和words其实是同一个对象.通过objects可以向列表中添加任意类型的对象,然后通过words试图得到一个string类型时就会出错.

    虽然IList<string>不可以转换为IList<object>,但是IEnumerable<string>却可以转换为IEnumerable<object>,代码如下:

    //虽然IList<string>不可以转换为IList<object>,但是IEnumerable<string>可以转换为IEnumerable<object>
    //从派生类泛型接口向基类泛型接口的转换称为协变 out T
    IEnumerable<string> words = new List<string> { "this", "is", "a", "string", "array" };
    IEnumerable<object> objects = words;

    C#允许从IEnumerable<string>到IEnumerable<object>的转换,是由于IEnumerable<T>接口没有修改数据的方法,不会出现前述的类型安全问题.

    这种从派生类泛型接口向基类泛型接口的转换称为协变.

    C#是如何知道什么情况下允许协变呢?这是通过泛型定义实现的.查看这两个泛型接口的定义,可以得到如下代码:

    public interface IEnumerable<out T> : IEnumerable
    {}
    public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
    {}

    在IEnumerable泛型接口的类型参数T前面有一个out关键字,而IList泛型接口则没有这个out关键字.泛型接口类型参数T前面的out表示T只出现在接口的输出位置(如方法的返回值),而不会出现在输入位置(如方法输入参数).

    像这种在类型参数前有out的泛型接口允许协变

    与协变相对的概念成为逆变,即将一个基类型的泛型接口转换为派生类型的泛型接口,代码如下

    //将一个基类型的泛型接口转换为派生类型的泛型接口
    //in T
    //用于指示协变和逆变的in out 关键字只能用于修饰泛型接口和委托的类型参数,不能用于类、不能用于类、结构、方法等。
    //所修饰的类型T必须是引用类型,不能是值类型
    IComparer<object> objectComparer = getObjectComparer();
    IComparer<string> stringComparer = getStringComparer();
    //逆变
    stringComparer = objectComparer;

    通常允许从派生类向基类的隐式转换,基类却不能隐式转换为派生类型.对于IComparer<T>接口来说,由基类接口向派生类接口转换是是有意义的.如代码中一个可以比较object的类型的比较器,自然可以成为比较string类型的比较器.查看

    IComparer泛型接口的定义,得到如下代码:

    public interface IComparer<in T>
    {}

    在IComparer泛型接口的参数T前面有个in关键字,表示此泛型接口中的参数只允许出现在输入位置(如方法的输入参数),而不允许出现在输出位置(如方法的返回值).

    像这种在类型参数前面有in的泛型接口允许逆变.

  • 相关阅读:
    testng
    RF相关命令
    批处理bat相关
    VIM常用快捷键
    JAVA异常处理
    cucumber+selenium
    webDriver各版本对应
    python源码
    python之logging模块
    pywinauto进阶练习
  • 原文地址:https://www.cnblogs.com/qingkongwanli/p/2789633.html
Copyright © 2020-2023  润新知