万博官网手机版登陆1、如果实例化泛型类型的参数相同

当前位置:万博官网手机版登陆 > 万博官网手机版登陆 > 万博官网手机版登陆1、如果实例化泛型类型的参数相同
作者: 万博官网手机版登陆|来源: http://www.shirokilaw.com|栏目:万博官网手机版登陆

文章关键词:万博官网手机版登陆,泛型类型

  这里所谓的“泛型方法的类型推断”,指的是根据已有的方法实参的类型,推断出泛型方法的类型实参。例如一个泛型方法 void Method(T[] args),如果我给出方法实参类型是 int[],那么希望能够推断出 T = int。

  这个问题是我在测试上一篇随笔《C# 使用 Binder 类自定义反射》中的类时发现的,当时为了能够让 PowerBinder 支持泛型方法绑定,完成了一些简单的类型推断工作,但是它只能支持直接使用泛型参数 T 作为参数类型,对于 T[],IList 这种复杂一些的情况是不能处理的。

  后来参考了《CSharp Language Specification》v5.0 中 7.5.2 类型推断一节,规范中给出了 C# 中进行类型推断的两阶段算法,算法分为两阶段主要是为了支持实参表达式和匿名函数的推断,而我的需求则要简单很多,只要支持普通的参数就可以了。又参考了 7.5.2.13 方法组转换的类型推断一节,最终得到了下面的简化算法。

  对于相应的封闭泛型方法的调用 Method(10),其中的 int 就是类型实参,10 就是方法实参。

  该算法首先认为所有 Xi 均未固定(即没有预设值),并从 D 的每个实参类型 Ui 到 M 的对应形参类型 Ti 进行下限推断(前提是 Ti 包含类型形参,即ContainsGenericParameters == true),但是如果 xi 为 ref 或 out 形参,则从 Ui 到 Ti 进行精确推断。如果没有为任何 Xi 找到界限,则类型推断将失败。否则,所有将 Xi 均固定到对应的 Si,它们是类型推断的结果。下面给出详细的推断算法,这里的算法经过了我的修改,与原规范并不完全相同。

  这里的精确推断指的是对于给定的实参类型 U,找到合适的形参类型 V,使得 U == V。

  否则,通过检查是否存在以下任何一种情况来确定集合 V1, …, V_k 和 U1, …, U_k:

  这里的下限推断指的是对于给定的实参类型 U,找到合适的形参类型 V,使得 V.IsImplicitFrom(U)。

  否则,如果 V 是数组类型 V1[…],U 是具有相同秩的数组类型 U1[…],或者 V 是一个 IEnumerable、ICollection 或 IList,U 是一维数组类型 U1[],如果不知道 U1 是引用类型,则从 U1 到 V1 进行精确推断,否则进行下限推断。

  否则,如果 V 是构造类、结构、接口或委托类型 C,并且存在唯一类型 C,使 U 等于、(直接或间接)继承自或者(直接或间接)实现 C(“唯一性”限制表示对于 interface C{} class U: C, C{},不进行从 U 到 C 的推断,因为 U1 可以是 X 或Y。),则从每个 Ui 到对应的 Vi 进行推断,如果不知道 U1 是引用类型,则进行精确推断,否则推断依赖于 C 的第 i 个类型参数:

  这里的上限推断指的是对于给定的实参类型 U,找到合适的形参类型 V,使得 U.IsImplicitFrom(V)。

  否则,如果 V 是数组类型 V1[…],U 是具有相同秩的数组类型 U1[…],或者 V 是一维数组类型 V1[],U 是一个 IEnumerable、ICollection 或IList,如果不知道 U1 是引用类型,则从 U1 到 V1 进行精确推断,否则进行上限推断。

  否则,如果 U 是构造类、结构、接口或委托类型 C,V 是等于、(直接或间接)继承自或者(直接或间接)实现唯一类型 C的类、结构、接口或委托类型(“唯一性”限制表示如果我们有 interface C{} class V: C, C{},则不进行从 C 到 V的推断。也不进行从 U1 到 X或 Y的推断。),则从每个 Ui 到对应的 Vi 进行推断,如果不知道 U1 是引用类型,则进行精确推断,否则推断依赖于 C 的第 i 个类型参数:

  然后我们依次检查 Xi 的每个界限:对于 Xi 的每个精确界限 U,将与 U 不同的所有类型 Ui 都从候选集中移除(要求 U == Ui)。对于 Xi 的每个下限U,将不存在从 U 进行的隐式转换的所有类型 Ui 都从候选集中移除(要求 Ui.IsImplicitFrom(U))。对于 Xi 的每个上限 U,将不存在从其到 U 进行的隐式转换的所有类型 Ui 都从候选集中移除(要求 U.IsImplicitFrom(Ui))。

  如果在剩下的候选类型 Ui 中,存在唯一类型 V,该类型可由其他所有候选类型经隐式转换而来,则将 Xi 固定到 V(也就是说,要求 V 是其中最通用的类型)。

  以上就是泛型方法的类型推断算法,其中只考虑了方法实参和方法形参一一对应的情况,如果需要处理 params T[] 参数,则需要对最后一个参数进行特殊处理,并分别使用 T 和 T[] 进行一次类型推断。做两次类型推断,就是为了判断是否是方法的展开形式的调用。

  这个算法的实现加上注释大概有 500 多行,这里就不再贴出,基本就是按照上面的 4 步来的,只是在一些细节上采用了更高效的做法。所有源码可以见这里。

  泛型:通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用。在.NET类库中处处都可以看到泛型的身影,尤其是数组和集合中,泛型的存在也大大提高了程序员的开发效率。更重要的是,C#的泛型比C++的模板使用更加安全,并且通过避免装箱和拆箱操作来达到性能提升的目的。因此,我们很有必要掌握并善用这个强大的语言特性。

  1、如果实例化泛型类型的参数相同,那么JIT编辑器会重复使用该类型,因此C#的动态泛型能力避免了C++静态模板可能导致的代码膨胀的问题。

  2、C#泛型类型携带有丰富的元数据,因此C#的泛型类型可以应用于强大的反射技术。

  3、C#的泛型采用“基类、接口、构造器,值类型/引用类型”的约束方式来实现对类型参数的“显示约束”,提高了类型安全的同时,也丧失了C++模板基于“签名”的隐式约束所具有的高灵活性

  .NET 中提供了很多判断某个类型或实例是某个类的子类或某个接口的实现类的方法,然而这事情一旦牵扯到泛型就没那么省心了。

  不过,如果你试图拿这个泛型版本的 typeof(Foo) 执行上述所有判断,你会发现所有的 if 条件都会是 false 。

  // 遍历类型实现的所有接口,判断是否存在某个接口是泛型,且是参数中指定的原始泛型的实例。

  于是,我们可以把这两个方法合成一个,用于实现类似 IsAssignableFrom 的效果,万博官网手机版登陆不过这回将支持原始接口(也就是 typeof(Foo))。

  /// 判断指定的类型 是否是指定泛型类型的子类型,或实现了指定泛型接口。

  /// 如果是泛型接口的子类型,则返回 true,否则返回 false。

  以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

  可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用。如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量。协变和逆变是两个相互对立的概念:

  在C# 4.0之前,所有的泛型类型都是不变量——即不支持将一个泛型类型替换为另一个泛型类型,即使它们之间拥有继承关系,简而言之,在C# 4.0之前的泛型都是不支持协变和逆变的。

  C# 4.0通过两个关键字:out和in来分别支持以协变和逆变的方式使用泛型。万博官网手机版登陆

  在上面的实例中,在C# 4.0之前是不能正常编译的,除了对赋值给基类集合时将子类集合做一个强制转换,但是在运行时仍然会抛出一个类型转换的异常。

  在上面的示例中我们Action类型的委托分配给类型Action的变量,根据逆变的定义我们可以知道Action类型是支持逆变的。

  为什么IEnumerable和Action可以分别支持类型的协变和逆变呢?我们查看这两个类型在中的定义:

  为了保证类型的安全,C#编译器对使用了out和in关键字的泛型参数添加了一些限制:

  支持协变(out)的类型参数只能用在输出位置:函数返回值、属性的get访问器以及委托参数的某些位置

  支持逆变(in)的类型参数只能用在输入位置:方法参数或委托参数的某些位置中出现。

  只有接口和委托可以拥有可变的类型参数。in和out修饰符只能用来修饰泛型接口和泛型委托。

  可变性只能用于引用类型,禁止任何值类型和用户定义的转换,如下面的转换是无效的:

  对于泛型类型参数来说,如果要将该类型的实参传给使用out或者ref关键字的方法,便不允许可变性,如:

  从实现上来说编译器完全可以自己判断哪些泛型参数能够逆变和协变,但实际却没有这么做,这是因为C#的开发团队认为:

  必须由开发者明确的指定可变性,因为这会促使开发者考虑他们的行为将会带来什么后果,从而思考他们的设计是否合理。

  在修改已有代码接口的可变性时,会有破坏当前代码的风险。例如,如果你依赖于不允许可变性的is或as操作符的结果,运行在.NET 4时,代码的行为将有所不同。同样,在某些情况下,因为有了更多可用的选项,重载决策也会选择不同的方法。所以在对已有代码引入可变性时要做好足够的单元测试以及防御措施。

  下面的代码能够通过编译,但是在运行时会抛出ArgumentException异常:

  这是因为负责链接多个委托的方法要求参数必须为相同的类型。上面的示例我们可以修改成如下正确的代码:

  版权申明:本文来源于网友收集或网友提供,如果有侵权,请转告版主或者留言,本公众号立即删除。

  泛型:通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用。在.NET类库中处处都可以看到泛型的身影,尤其是数组和集合中,泛型的存在也大大提高了程序员的开发效率。更重要的是,C#的泛型比C++的模板使用更加安全,并且通过避免装箱和拆箱操作来达到性能提升的目的。因此,我们很有必要掌握并善用这个强大的语言特性。

  1、如果实例化泛型类型的参数相同,那么JIT编辑器会重复使用该类型,因此C#的动态泛型能力避免了C++静态模板可能导致的代码膨胀的问题。

  2、C#泛型类型携带有丰富的元数据,因此C#的泛型类型可以应用于强大的反射技术。

  3、C#的泛型采用“基类、接口、构造器,值类型/引用类型”的约束方式来实现对类型参数的“显示约束”,提高了类型安全的同时,也丧失了C++模板基于“签名”的隐式约束所具有的高灵活性

  .NET 中提供了很多判断某个类型或实例是某个类的子类或某个接口的实现类的方法,然而这事情一旦牵扯到泛型就没那么省心了。

  不过,如果你试图拿这个泛型版本的 typeof(Foo) 执行上述所有判断,你会发现所有的 if 条件都会是 false 。

  于是,我们可以把这两个方法合成一个,用于实现类似 IsAssignableFrom 的效果,不过这回将支持原始接口(也就是 typeof(Foo))。

  以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对华域联盟的支持。

网友评论

我的2016年度评论盘点
还没有评论,快来抢沙发吧!