你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

子类型化和方差

Q# 仅支持几个转换机制。 仅当应用二进制运算符、计算条件表达式或构造数组文本时,才会发生隐式转换。 在这些情况下,将确定一个常见的超类型,并自动执行必要的转换。 除了此类隐式转换之外,还可以通过函数调用进行显式转换,而且通常是必需的。

目前,存在的唯一子类型关系适用于作。 直观地说,应该允许一个作替换支持多个所需函数集的作。 具体来说,对于任何两种具体类型 TInTOut,子类型的关系是

    (TIn => TOut) :>
    (TIn => TOut is Adj), (TIn => TOut is Ctl) :>
    (TIn => TOut is Adj + Ctl)

其中 A :> B 指示 BA的子类型。 短语不同,BA 更严格,因此,无论需要 A 类型的值,都可以使用类型 B 的值。 如果可调用对象依赖于类型为 A的参数(项),则可以安全地替换 B 类型的参数,因为前提是提供了所有必要的功能。

这种类型的多态性扩展到元组,即类型 B 元组是元组类型的子类型,如果元组类型包含的项数相同,并且每个项的类型是 A中相应项类型的子类型,则 A。 这称为 深度子级。 目前不支持 宽度子类型,即任何两种 struct 类型或 struct 类型与任何内置类型之间没有子类型关系。 存在 unwrap 运算符,该运算符允许提取包含所有命名项的元组,因此会阻止此作。

注释

对于可调用对象,如果可调用方处理 A类型的参数,则它还能够处理类型为 B的参数。 如果将可调用对象作为参数传递给另一个可调用对象,则必须能够处理类型签名可能需要的任何内容。 这意味着,如果可调用对象需要能够处理 B类型的参数,则可以安全地传递能够处理类型 A 的更常规参数的任何可调用对象。 相反,我们期望,如果要求传递的可调用方返回类型为 A的值,则返回类型 B 的值的承诺就足够了,因为该值将提供所有必要的功能。

作或函数类型在其参数类型中 逆变,并在其返回类型中 协变。 因此,A :> B 意味着对于任何具体类型 T1

    (B → T1) :> (A → T1), and
    (T1 → A) :> (T1 → B) 

此处 可能意味着函数或作,我们省略特征的任何批注。 分别用 (B → T2)(T2 → A) 替换 A,并分别用 (A → T2)(T2 → B) 替换 B,得出的结论是,对于任何具体类型 T2

    ((A → T2) → T1) :> ((B → T2) → T1), and
    ((T2 → B) → T1) :> ((T2 → A) → T1), and
    (T1 → (B → T2)) :> (T1 → (A → T2)), and
    (T1 → (T2 → A)) :> (T1 → (T2 → B)) 

通过上岗,它遵循每个附加间接反转参数类型的方差,并使返回类型的方差保持不变。

注释

这也清楚地说明了数组的方差行为:通过项访问运算符检索项对应于调用 (Int -> TItem)类型的函数,其中 TItem 是数组中元素的类型。 由于此函数在传递数组时隐式传递,因此它遵循该数组需要在其项类型中协变。 元组也保留相同的注意事项,这些元组是不可变的,因此对于每个项类型而言都是协变的。 如果数组不是不可变的,则存在允许在数组中设置项的构造,因此采用类型为 TItem的参数意味着数组也需要逆变。 支持获取和设置项的数据类型的唯一选项是 固定,这意味着没有任何子关系:即使 BA的子类型,B[] 也不 A[] 子类型。 尽管 Q# 中的数组 不可变,但它们是固定的,而不是协变的。 例如,这意味着无法将类型 (Qubit => Unit is Adj)[] 的值传递给需要类型 (Qubit => Unit)[]参数的可调用对象。 保持数组不变可以更灵活地与数组在运行时的处理和优化方式相关,但将来可能会修改该数组。