Сопоставление с образцом на обобщениях в неуниверсальном методе, реализующем интерфейс - PullRequest
1 голос
/ 04 июля 2010

У меня неприятная проблема. Я создаю движок представления в ASP.NET MVC и реализую интерфейс IViewEngine. В одном из методов я пытаюсь динамически определить тип результата представления. Иногда результатом является шаблон (с шаблоном типа <'key>). Ключи используются для нацеливания на заполнитель в шаблоне, и идея состоит в том, чтобы использовать дискриминационное объединение, потенциально уникальное для каждого веб-сайта. Это может выглядеть так:

type MasterKey = | HeadContent | HeaderContent | MainContent | FooterContent
let MasterTemplate : Template<MasterKeys> = ...

Теперь проблема заключается в следующем: поскольку я реализую интерфейс, я не контролирую сигнатуру метода. Поскольку я не могу добавить параметр универсального типа, 'a будет преобразован в объект, и шаблон не будет соответствовать приведенному ниже:

   match result with
   | :? foo -> ...
   | :? bar -> ...
   | :? Template<'a> -> ...

Есть идеи?

Ответы [ 2 ]

6 голосов
/ 04 июля 2010

К сожалению, нет способа сделать это красиво.Если у вас есть контроль над типом Template<'T>, то лучше всего создать неуниверсальный интерфейс (например, ITemplate) и реализовать его в типе Template<'T>.Тогда вы можете просто проверить интерфейс:

| :? ITemplate as t -> ...

Если это не так, тогда ваш единственный вариант - использовать магию отражения.Вы можете реализовать активный шаблон, который соответствует, когда тип имеет какое-то значение Template<'T>, и возвращает список типов (System.Type объектов), которые использовались в качестве общих аргументов.В вашем псевдокоде вы хотели получить это как параметр общего типа 'a - это невозможно получить как параметр типа времени компиляции, но вы можете получить его как информацию о типе во время выполнения:

let (|GenericTemplate|_|) l =
  let lty = typeof<list<int>>.GetGenericTypeDefinition()
  let aty = l.GetType()
  if aty.IsGenericType && aty.GetGenericTypeDefinition() = lty then
    Some(aty.GetGenericArguments())
  else 
    None

Теперь вы можете написать следующий код сопоставления с шаблоном:

match result with
| GenericTemplate tys ->
    // ...

Последняя проблема - как вы можете использовать эту информацию о типе среды выполнения для запуска некоторого универсального кода.Лучший вариант, который я могу придумать, - это вызвать универсальный метод (или функцию), используя отражение - тогда вы можете указать информацию о типе среды выполнения в качестве универсального параметра, и поэтому код может быть универсальным.Самый простой вариант - вызвать статический член типа:

 type TemplateHandler =
   static member Handle<'T>(arg:Template<'T>) =
     // This is a standard generic method that will be 
     // called from the pattern matching - you can write generic
     // body of the case here...
     "aaa"

| :? GenericTemplate tys ->
    // Invoke generic method dynamically using reflection
    let gmet = typeof<TemplateHandler>.GetMethod("Handle").MakeGenericMethod(tys)
    gmet.Invoke(null, [| result |]) :?> string // Cast the result to some type

Основная идея заключается в том, что вы перемещаете тело сопоставления с образцом (у которого не может быть параметров универсального типа) в метод (который может иметь универсальный тип).параметры) и динамически запускать метод с использованием отражения.

Вы можете изменить код, чтобы использовать функцию let вместо static member - найти функцию с помощью отражения немного сложнее.

1 голос
/ 04 июля 2010

Можете ли вы сделать общий класс механизма представления общим в соответствии с типом 'key, который он использует?Индивидуальные проекты должны будут унаследовать от вашего класса механизма представления и указать тип ключа в процессе.

...