模式结合类型测试和文字

时间:2012-08-16 14:28:27

标签: f# f#-3.0 active-pattern guard-clause

升级到VS 2012 RTM后,this question中的活动模式无法编译。它提供了一种进行类型测试并在单个模式中匹配文字的方法。例如:

let (|Value|_|) value = 
  match box value with
  | :? 'T as x -> Some x
  | _ -> None

let getValue (name: string) (r: IDataReader) =
  match r.[name] with
  | null | :? DBNull | Value "" -> Unchecked.defaultof<_>
  | v -> unbox v

这可以在没有活动模式的情况下完成吗?我意识到可以使用when守卫(:? string as s when s = ""),但它不能与其他模式结合使用。

2 个答案:

答案 0 :(得分:1)

您应该能够使用参数化的活动模式:

let (|Value|_|) v x = 
    if unbox x = v then 
        Some() 
    else None

用法应该与您现在的用法完全一样。

修改

虽然我不知道突破性更改是否是故意的,但我认为通常应避免使用与输入类型无关的泛型返回类型的活动模式。与类型推断结合使用时,它们可以轻松掩盖细微的错误。请考虑以下示例,使用原始的(|Value|_|)模式:

match [1] with
| Value [_] -> "Singleton"
| _ -> "Huh?"

这似乎不是你真正尝试过的东西 - 这个名字意味着Value只应该用于文字;参数化的活动模式可以实现这种情况。

答案 1 :(得分:1)

kvb的变体(由于假设类型测试成功,因此不会做同样的事情)可以修改以产生类似的模式:

let (|Value|_|) x value =
  match box value with
  | :? 'T as y when x = y -> Some()
  | _ -> None

然而,存在微妙的性能差异。原始活动模式转换为:

public static FSharpOption<T> |Value|_|<a, T>(a value)
{
    object obj = value;
    if (!LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric<T>(obj))
    {
        return null;
    }
    return FSharpOption<T>.Some((T)((object)obj));
}

即,它进行了类型测试和强制转换。它的用法(match x with Value "" -> ...)转换为:

FSharpOption<string> fSharpOption = MyModule.|Value|_|<object, string>(obj);
if (fSharpOption != null && string.Equals(fSharpOption.Value, ""))
{
    ...
}

最值得注意的是,模式返回的类型值使用模式的典型编译器转换进行匹配(string.Equals表示字符串)。

更新的模式转换为:

public static FSharpOption<Unit> |Value|_|<T, a>(T x, a value)
{
    object obj = value;
    if (LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric<T>(obj))
    {
        T y = (T)((object)obj);
        T y3 = y;
        if (LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<T>(x, y3))
        {
            T y2 = (T)((object)obj);
            return FSharpOption<Unit>.Some(null);
        }
    }
    return null;
}

使用泛型相等,效率低于文字匹配。由于将相等性融入模式中,因此用法稍微简单一些:

FSharpOption<Unit> fSharpOption = MyModule.|Value|_|<string, object>("", obj);
if (fSharpOption != null)
{
    ...
}

无论如何,它有效。但我更喜欢原版。