Но этот матч у меня часть большого comp expression, а вводить типы во внешней функции совсем не хочется.
Есть ли обходные варианты? Сейчас беру типы из GetGenericArguments()
Здравствуйте, Аноним, Вы писали:
А>Такая штука штука не работает.
Мне кажется, вы хотите сделать что-то противоречащее логике системы типов .NET. В рантайме нельзя спросить, имеет ли объект тип IDictionary[чегототам, чегототам]. Нужно точно знать конкретный тип с подставленными конкретными параметрами (кажется это называется closed generic type).
Но можно матчить просто по IEnumerable, потому что IEnumerable[T] подтип IEnumerable. С IDictionary[K, V] хуже; тем не менее, большинство объектов, которые реализуют IDictionary[K, V], также реализуют IDictionary, так что можно матчить просто по IDictionary.
C>Мне кажется, вы хотите сделать что-то противоречащее логике системы типов .NET. В рантайме нельзя спросить, имеет ли объект тип IDictionary[чегототам, чегототам]. Нужно точно знать конкретный тип с подставленными конкретными параметрами (кажется это называется closed generic type).
С проверкой всё не так уж и сложно:
def d = Dictionary.[int, string]();
// | d is Dictionary[TKey, TValue] => {}
Console.WriteLine(d.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>)));
// | d is Dictionary[int, TValue] => {}
Console.WriteLine(d.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>))
&& d.GetType().GetGenericArguments()[0] == typeof(int));
А вот как эти типы "продвинуть" в тело матча, я не представляю. Мне кажется макросом тут уже не обойдёшься.
А>Такая штука штука не работает. В принципе понятно, что компилятору нужно знать откуда объявились ['k, 'v].
Дело в том, что паттерн is не просто проверяет реализует ли тип некоторый интерфейс, но и, в случае успеха, приводит тип к нему. А привести значение к открытому дженерик-типу невозможно. Это станет понятно, если вместо "_" использовать паттер "переменная":
| x is IDictionary['k,'v] => // Какой будет тип у "x"?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
I>def d = Dictionary.[int, string]();
I>// | d is Dictionary[TKey, TValue] => {}
I>Console.WriteLine(d.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>)));
I>// | d is Dictionary[int, TValue] => {}
I>Console.WriteLine(d.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>))
I> && d.GetType().GetGenericArguments()[0] == typeof(int));
I>
I>А вот как эти типы "продвинуть" в тело матча, я не представляю. Мне кажется макросом тут уже не обойдёшься.
Ну, почему? Макросами можно решить много чего, в том числе, и эту задачу .
Можно создать макрос typematch (аналог match) который перепишет паттерны в приведенный выше код. Если это удовлетворит (т.е. если само значение не нужно приводить к этому типу), то это решит проблему. В прочем с тем же успехом можно просто написать:
def m()
{
...
def t : object = ...;
match(d.GetType().GetGenericTypeDefinition())
{
| x when x.IsAssignableFrom(typeof(IDictionary[_,_])) => {}
| x when x.IsAssignableFrom(typeof(IEnumerable[_])) => {}
| _ => {}
}
...
}
Еще можно воспользоваться активными паттернами. Это аналогично введению своего макроса, только значительно проще.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Ну, почему? Макросами можно решить много чего, в том числе, и эту задачу . VD>Можно создать макрос typematch (аналог match) который перепишет паттерны в приведенный выше код. Если это удовлетворит (т.е. если само значение не нужно приводить к этому типу), то это решит проблему.
Сравнение и правда несложно макросом описать. А вот как сделать нечто вроде этого:
match(t) // t : object
{
| t is Dictionary['t,'k] => {
def l = List.['t]();
}
}
С одной стороны, понятно, что это несколько протеворечит системе типов .NET. А с другой, мне кажется, очень органично вписывается в паттерн матчинг.
VD>Дело в том, что паттерн is не просто проверяет реализует ли тип некоторый интерфейс, но и, в случае успеха, приводит тип к нему. А привести значение к открытому дженерик-типу невозможно. Это станет понятно, если вместо "_" использовать паттер "переменная": VD>
VD> | x is IDictionary['k,'v] => // Какой будет тип у "x"?
VD>
Я уже ответил выше, чего хотелось бы добиться. Теоретически, это должно быть решаемо (правда на уровне компилатора).
Мы ведь работаем с дженерик типами в методах:
Здравствуйте, ionoy, Вы писали:
I>Но тогда t1 и t2 будут объектами Type, т.е. чтобы из них сделать новый экземпляр дженерик типа придётся лезть в рефлекшн.
Да, теоретически можно написать генерик-метод, и вызывать его через рефлекшн. Но это чудо не стоит делать частью языка или стандартной библиотеки
Здравствуйте, ionoy, Вы писали:
I>Но тогда t1 и t2 будут объектами Type, т.е. чтобы из них сделать новый экземпляр дженерик типа придётся лезть в рефлекшн.
Без рефлекшона, то что ты хочешь на дотнете не сделать. Причем это еще будет не очень то и быстро.
Лучше опиши саму задачу (а не часть ее решения). Возможно есть лучше методы ее решения.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Лучше опиши саму задачу (а не часть ее решения). Возможно есть лучше методы ее решения.
Мне надо обойти сложный объект (в данном случае IoC контейнер), и найдя в нём определённые типы заменить их на свои собственные.
На самом деле приведённый код мне не так уж и нужен, вопрос я задал скорее из спортивного интереса.
Здравствуйте, ionoy, Вы писали:
I>Мне надо обойти сложный объект (в данном случае IoC контейнер), и найдя в нём определённые типы заменить их на свои собственные. I>На самом деле приведённый код мне не так уж и нужен, вопрос я задал скорее из спортивного интереса.
Если типы известны во время компиляции, то код обхода и замены можно сгенерировать автоматически.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Если типы известны во время компиляции, то код обхода и замены можно сгенерировать автоматически.
В таком случае код макроса будет не сильно отличаться от кода обхода, если я не ошибаюсь.
Ну и главный недостаток такого подхода в том, что такая ситуация разрулится неверно:
public class A
{
private _b : IB;
this() {
_b = B();
}
}
Поэтому ходить желательно по инстансу в рантайме, только тогда будут известны настоящие типы.
Здравствуйте, ionoy, Вы писали:
I>В таком случае код макроса будет не сильно отличаться от кода обхода, если я не ошибаюсь.
Макрос может генерировать код по ДСЛ или другой мета-модели. Т.е. другой уровень кода.
I>Ну и главный недостаток такого подхода в том, что такая ситуация разрулится неверно: I>
I>public class A
I>{
I> private _b : IB;
I> this() {
I> _b = B();
I> }
I>}
I>
I>Поэтому ходить желательно по инстансу в рантайме, только тогда будут известны настоящие типы.
Макрос может находить (статически) все подтипы и генерировать проверку для них. Так что это не сработает только при одном условии — подтипы могут появляться в рантайме (путем загрузки внешних сборок).
Можно все таки описать исходную задачу? Какова цель обхода?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Можно все таки описать исходную задачу? Какова цель обхода?
В общем я делаю некий аналог jRebel для .NET. Это такая штука которая позволяет без явной перекомпиляции изменять код веб приложения. То есть изменил ты что-то в контроллере, нажал ctrl+s и получил обновлённое значение в браузере. Все это без потери HttpContext, сессии и прочего.
Для того, чтобы этого добиться, я компилирую новую сборку при изменении файлов исходников. Но даю ей другое имя, чтобы не было конфликтов типов. Потом подгружаю её в текущий AppDomain и подсовываю "новый" контроллер при следующем запросе. Там ещё много тонкостей, в основном связанных с тем, что типы из views-сборки должны соответствовать типам из сборки приложения, но это уже готово.
Короче практически всё работает, осталась проблема с IoC контейнерами. Типы в них остаются из старой сборки. Инициализировать их заново — это лишняя работа для пользователя библиотеки, так что желательно заменить типы автоматически.
Тонкость здесь вот в чём: конкретных типов контейнеров я не знаю, иначе мне пришлось бы добавлять все существующие IoC решения к себе в референсы и ориентироваться на них. Вместо этого я просто беру контейнер как object, а там уже с помощью рефлекшена произвожу раскопки.
Так что генерировать дерево типов на стадии компилации скорее всего не выйдет.
Здравствуйте, ionoy, Вы писали:
I>Тонкость здесь вот в чём: конкретных типов контейнеров я не знаю, иначе мне пришлось бы добавлять все существующие IoC решения к себе в референсы и ориентироваться на них. Вместо этого я просто беру контейнер как object, а там уже с помощью рефлекшена произвожу раскопки.
Очень багодромное решение.
А почему не подходит перезагрузка домена как а АСП.НЕТ?
I>Так что генерировать дерево типов на стадии компилации скорее всего не выйдет.
Да. Тут только через рефлекшон можно что-то сделать.
Макросы могут только помочь скрыть некрасивости под более читабельным синтаксисом.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Очень багодромное решение.
Согласен, но моя цель сделать инструмент максимально прозрачным для программиста. Сейчас достаточно вызвать Init метод в Application_Start, хотелось бы, чтоб так это и осталось. Разные контейнеры я могу отличать по имени типа, а значит изменять немного логику, если понадобится.
VD>А почему не подходит перезагрузка домена как а АСП.НЕТ?
Если перегрузить домен, то все статические члены обнулятся. Это значит, что нужно всё заново инициализировать, а инициализация в некоторых приложениях может 10-15 секунд занимать. Не лучший дебаг экспириенс. Плюс потеря сессии, HttpContext и прочего.
Здравствуйте, ionoy, Вы писали:
I>В общем я делаю некий аналог jRebel для .NET. Это такая штука которая позволяет без явной перекомпиляции изменять код веб приложения. То есть изменил ты что-то в контроллере, нажал ctrl+s и получил обновлённое значение в браузере. Все это без потери HttpContext, сессии и прочего.
Давно подумывал о чем-то подобном, но руки не доходят. После рельс, постоянная перекомпиляция веб-приложения реально мешает разработке и выбивает из ритма. Решение частное или сможешь выложить в общий доступ?
Z>Давно подумывал о чем-то подобном, но руки не доходят. После рельс, постоянная перекомпиляция веб-приложения реально мешает разработке и выбивает из ритма. Решение частное или сможешь выложить в общий доступ?
Выложу, как только разберусь с DI контейнерами. Хотя, если хочешь посмотреть, можно сейчас уже сделать приватный репо.
Там ещё одна проблема осталась, копирование статичных данных в новую сборку.
ну и как всегда -- епрст, кто же *так* документацию пишет, на уровне шифровки
пример должен быть мотивированным, скажем простой пример может быть таким:
Console.WriteLine("Note, that y="); Console.WriteLine(y); /// это покороче надо записать, но я ваш синтаксис не помню
active match(y)
{
| Product(2,x) => Console.WriteLine("Here is an integer x, that 2*x=y; such x = "); Console.WriteLine(x);
| _ => Console.WriteLine("Sorry, there is no such an integer x, that 2*x=y");
}
почему именно от 4 потребовалось отнимать 2, совершенно не понятно, в то время как то, что только четные числа делятся на 2 придает мотивированность
и наконец -- можно ли писать 2*х вместо Product(2,x)? можно ли (рекурсивно) писать 3*х+1 ?
Здравствуйте, ionoy, Вы писали:
I>Выложу, как только разберусь с DI контейнерами. Хотя, если хочешь посмотреть, можно сейчас уже сделать приватный репо. I>Там ещё одна проблема осталась, копирование статичных данных в новую сборку.
Да мне не к спеху, все равно сейчас на рельсах пишу, в моно разве что потестить могу, если развернуть можно быстро.
Здравствуйте, Ziaw, Вы писали:
Z>Здравствуйте, ionoy, Вы писали:
Z>Да мне не к спеху, все равно сейчас на рельсах пишу, в моно разве что потестить могу, если развернуть можно быстро.
Здравствуйте, catbert, Вы писали:
C>А что с NRails кстати?
Да ничего. Я приостановил работу, когда понял, что проблем с интеграцией в 2008 студию будет больше чем с самим движком, а 2008 в тот момент стремительно уходила в небытие. Сейчас уже можно было бы пробовать написать интеграцию для 2010, но у меня нет задач, в которых я мог бы применить nrails. Если бы было время и задачи, я бы сейчас не стал завязываться на ASP.NET MVC, а сделал бы отдельные модули для генерации типов и маппинга, для роутинга, для генерации view. И применил бы все для NancyFx, который не требует специальных заточек студии и хостится в чем угодно.
Но это теория, которая разбивается о тот факт, что у меня нет подходящих задач, в которых я мог бы обкатать технологию. Веб я делаю на RoR и вижу, что они на несколько шагов опережают майкрософтовские технологии. Потеря статики печальна, но, как оказалось, не является серьезной проблемой в небольших сайтах, зато дает большой буст к скорости разработки.
Возможно система перекомпиляции на лету от ionoy, даст какую-то базу, для построения похожего по юзабилити решения, но все равно, есть куча моментов, которые на решениях от ms приходится делать через задницу. И есть куча прикладных инфраструктурных задач, которые требуют реализации и поддержки (организация скриптов и стилей, трансляция в javascript, вменяемый роутинг). Я не могу столько времени уделять на хобби.
Я убежден, что на nemerle можно создать фреймворк, не уступающий рельсам по удобству и скорости разработки, но дающий статический контроль практически для всех коммуникаций, но не требующий написания лишнего кода для обеспечения этого. Но для этого нужны ресурсы, которыми на данный момент я не располагаю.