Образец, сочетающий тип теста и литерал
Активный шаблон в этом вопросе не удается скомпилировать после обновления до 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 = ""
), но это не может быть объединено с другими образцами.
2 ответа
Вариант 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
должен использоваться только с литералами; параметризованные активные шаблоны включают именно этот сценарий.