此问题中的活动模式在升级到VS 2012 RTM后无法编译。它提供了一种进行类型测试并在单个模式中匹配文本的方法。例如:
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 = ""
),但它不能与其他模式结合使用。
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)
{
...
}
无论如何,它有效。但我更喜欢原版。
您应该能够使用参数化的活动模式:
let (|Value|_|) v x =
if unbox x = v then
Some()
else None
用法应该看起来与您现在拥有的完全一样。
编辑
虽然我不知道中断性更改是否是故意的,但我认为通常应该避免使用与输入类型无关的泛型返回类型的活动模式。 当与类型推断结合使用时,它们可以轻松掩盖细微的错误。 请考虑以下示例,使用原始(|Value|_|)
模式:
match [1] with
| Value [_] -> "Singleton"
| _ -> "Huh?"
这似乎不是你真正会尝试的东西 - 这个名字意味着Value
应该只与文字一起使用;参数化的活动模式正是可以实现这种情况。