前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C# 学习笔记(15)—— C# 4.0

C# 学习笔记(15)—— C# 4.0

作者头像
Karl Du
发布2023-10-20 18:51:58
1870
发布2023-10-20 18:51:58
举报
文章被收录于专栏:Web开发之路Web开发之路

可选参数和命名实参

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

可选参数

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

代码语言:javascript
复制
static void Test(int x, int y = 10, string name = "")
{
}

在以上代码中,参数 x 是必选参数,即调用方法必须为其指定实参;而参数 y 和参数 name 为可选参数,即可以不用为它们指定实参

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

  • 所有可选参数必须位于必选参数之后
  • 可选参数的默认值必须为常量,如数字、常量字符串、null、const 成员和枚举成员等
  • 参数数组不能为可选参数
  • refout关键字标识的参数不能被设置为可选参数

命名实参

当调用带有可选参数的方法时,如果我们省略了一个参数,编译器默认为我们省略的是最后一个参数。但是如果我们只想省略第二个参数怎么办?下面的代码是使用命名实参的一个简单例子

代码语言:javascript
复制
class Program
{
  static void Main(string[] args)
  {
    Test(10, name: "Hello");
    Test(10, y: 10, name: "Hello");
  }
  static void Test(int x, int y = 10, string name = "")
  {
    Console.WriteLine($"{name}:{x},{y}");
  }
}

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

泛型的可变性

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

代码语言:javascript
复制
string[] strs = new string[3];
object[] objs = strs;

那么,泛型中的泛型参数是否也支持这样的转换呢?C# 2.0 确实是不支持的,但因为有了这样的需求,微软便适应地做出了改进,在 C# 4.0 中引入了泛型的协变性和逆变性

协变性

协变性指的是泛型类型参数可以从一个派生类隐式地转换为基类

C# 4.0 引入out关键字来标注泛型参数,以示支持协变性,为了更好的说明,下面使用 .Net 类库中的public interface IEnumerable<out T>接口为例,做泛型协变性的演示:

代码语言:javascript
复制
class Program
{
  static void Main(string[] args)
  {
    List<object> listObject = new List<object>();
    List<string> listStr = new List<string>();
    
    listObject.AddRange(listStr); // 成功
    listStr.AddRange(listObject); // 失败
  }
}

协变性很好理解,派生类可以隐式转换为基类,反之则失败

逆变性

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

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            List<object> listObject = new List<object>();
            List<string> listString = new List<string>();

            IComparer<object> objComparer = new TestComparer();
            IComparer<string> stringComparer = new TestComparer();

            listString.Sort(objComparer);
            listObject.Sort(stringComparer); // 编译报错

            Console.ReadKey();
        }
    }

    public class TestComparer : IComparer<object>
    {
        public int Compare([AllowNull] object x, [AllowNull] object y)
        {
            return x.ToString().CompareTo(y.ToString());
        }
    }
}

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

listObject变量的Sort方法则应接受IComparer<Object>类型参数,但代码listObject.Sort(objComparer2)却传入了IComparer<string>类型的变量。由于IComparer<in T>接口泛型参数只支持逆变,不支持协变,所以不能把IComparer<string>类型隐式地转换为IComparer<object>,所以会出现编译错误

协变和逆变的注意事项

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

  • 只有借口和委托才支持协变和逆变(如Func<out TResult>Action<in T>),类或泛型方法的类型参数都不支持协变和逆变
  • 协变和逆变只适用于引用类型,值类型不支持协变和逆变(因为可变性存在引用转换的过程,而值类型变量存储的就是对象本身,并不是对象的应用),所以List<int>无法转换为IEnumerable<object>
  • 必须显示地使用inout来标记类型参数
  • 委托的可变性不要在多播委托中使用
本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021/08/11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 可选参数和命名实参
    • 可选参数
      • 命名实参
      • 泛型的可变性
        • 协变性
          • 逆变性
            • 协变和逆变的注意事项
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
            http://www.vxiaotou.com