2010-07-04 2 views
1

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

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

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

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

Любые идеи?

ответ

1

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

+0

Это оказалось отличной идеей во всей простоте! Я сделал, как вы сказали, добавили эту строку в Global.asax.cs: ViewEngines.Engines.Add (новый WingBeats.Mvc.WingBeatsTemplateEngine ()); Спасибо! (Я чувствую себя немного глупо, чтобы не придумать это решение самостоятельно, хотя я боролся часами!) –

6

К сожалению, нет никакого способа сделать это красиво. Если у вас есть контроль над типом 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, а также - это немного сложнее найти функцию с использованием отражения.

+0

Мне нравится этот подход, но вы можете сделать активный шаблон немного более общим, параметризируя его на основе определения общего типа для соответствия. См. Http://stackoverflow.com/questions/2140079/how-to-cast-an-object-to-a-list-of-generic-type-in-f. – kvb

+0

@kvb: Хороший трюк! Я пробовал писать общий шаблон, но, похоже, нет способа указать параметр типа в использовании.Использование котировок, по-видимому, хорошо работает (или, возможно, параметр может быть просто «(typedefof >))? –

+0

В шаблонах допускается ограниченное подмножество элементов языка (даже для параметров для активных шаблонов), поэтому я не думаю вы можете напрямую передать тип. Насколько я могу судить, использование цитаты - это самое чистое решение, разрешенное языком, но я бы хотел, чтобы его доказали неправильно! – kvb