F# 结合类型测试和文本的模式

F# 结合类型测试和文本的模式,f#,f#-3.0,active-pattern,guard-clause,F#,F# 3.0,Active Pattern,Guard Clause,升级到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

升级到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
let(|值| | |)值=
将框值与
| :? 'T as x->Some x
|无
let getValue(名称:字符串)(r:IDataReader)=
将r.[名称]与匹配
|空|::?DBNull |值“”->未选中的.defaultof
|v->unbox v

在没有活动模式的情况下是否可以执行此操作?我意识到可以使用
when
保护(
:?字符串为s when s=”“
),但它不能与其他模式组合。

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

let (|Value|_|) v x = 
    if unbox x = v then 
        Some() 
    else None
FSharpOption<Unit> fSharpOption = MyModule.|Value|_|<string, object>("", obj);
if (fSharpOption != null)
{
    ...
}
用法应该与您现在的用法完全相同

编辑

虽然我不知道这种突破性的改变是否是有意的,但我相信通常应该避免使用与输入类型无关的通用返回类型的活动模式。当与类型推断相结合时,它们可以很容易地掩盖细微的错误。考虑下面的例子,使用您的原始<代码>(值V*)/代码>模式:

match [1] with
| Value [_] -> "Singleton"
| _ -> "Huh?"
这似乎不是你真正尝试过的东西——这个名字意味着
Value
应该只与文本一起使用;参数化的活动模式正好支持此场景。

可以修改kvb的变体(因为它假设类型测试成功,所以做的事情并不完全相同),以生成类似的模式:

让(|值| | |)x值=
将框值与
| :? '当x=y->Some()
|无
然而,有一个微妙的性能差异。原始活动模式转换为:

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));
}
FSharpOption<string> fSharpOption = MyModule.|Value|_|<object, string>(obj);
if (fSharpOption != null && string.Equals(fSharpOption.Value, ""))
{
    ...
}
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;
}
最值得注意的是,使用模式的典型编译器转换(
string.Equals
用于字符串)匹配从模式返回的类型化值

更新后的模式转换为:

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));
}
FSharpOption<string> fSharpOption = MyModule.|Value|_|<object, string>(obj);
if (fSharpOption != null && string.Equals(fSharpOption.Value, ""))
{
    ...
}
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;
}
publicstaticfsharpOption | Value | | |(tx,a值)
{
对象obj=值;
if(LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric(obj))
{
T y=(T)((对象)obj);
T y3=y;
if(LanguagePrimitives.HashCompare.GenericeQualityInquired(x,y3))
{
T y2=(T)((对象)obj);
返回FSharpOption.Some(null);
}
}
返回null;
}
它使用泛型相等,并且效率低于对文本的匹配。由于模式中烘焙了相等项,因此用法更简单:

let (|Value|_|) v x = 
    if unbox x = v then 
        Some() 
    else None
FSharpOption<Unit> fSharpOption = MyModule.|Value|_|<string, object>("", obj);
if (fSharpOption != null)
{
    ...
}
FSharpOption FSharpOption=MyModule.|值| | |(“”,obj);
如果(fSharpOption!=null)
{
...
}

不管怎样,它是有效的。但我更喜欢原版。

您的解决方案有点不同,但它提供了很好的方向。请参阅我的答案以获得兼容的变体。好的‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌我认为你的反例有点像稻草人
(| Value | | |)
,或其返回类型中的任何泛型函数,都需要注意推断的类型。您的示例进一步说明了
+
,即除非是内联的,否则其操作数被推断为
int
。您是否也认为不允许使用以下活动模式
let(| X |)X=unbox X
这是2.0版的另一个例子,但不是3.0版。我改变了我的例子-我希望这能更清楚地说明我认为有问题的错误类型。这很有效,但也不允许使用嵌套模式(因为参数化活动模式的参数是表达式求值的,而不是匹配的模式)。对。这是不同等式语义的根本原因,但您可以更简洁地说。