05.08.2015, 15:54 | #1 |
Участник
|
Ax2012. Типы операндов не совместимы с оператором
Использование функций при вычислении значений тринарных операторов в Ax2012 R3 в процессе компиляции выдает предупреждение:
Типы операндов не совместимы с оператором Как это можно вылечить, оставаясь в рамках тринарных операторов? X++: static void test(Args _args) { Qty qty; Qty qty2; ; qty = true ? qty2 : 0.0; // Нет предупреждений qty = true ? min(qty2,qty) : 0.0; // предупреждение qty = true ? 0.0 : min(qty2,qty); // предупреждение }
__________________
- Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... |
|
05.08.2015, 16:12 | #2 |
Участник
|
Не пробовал, но как насчет "any2real(min(qty2,qty)"?
|
|
|
За это сообщение автора поблагодарили: Владимир Максимов (5). |
05.08.2015, 16:17 | #3 |
Участник
|
Вот ведь... Действительно, приведение типов у функции min() помогло. Спасибо...
__________________
- Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... |
|
05.08.2015, 16:45 | #4 |
Участник
|
А как быть в случае табличных переменных и map? any2record() вроде бы нет?
X++: CustVendTable = true ? VendTable::find(...) : CustTable::find(...);
__________________
- Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... |
|
05.08.2015, 17:48 | #5 |
Участник
|
На последний вопрос сам спросил - сам ответил Нужно использовать метод data() соответствующей табличной переменной или MAP.
X++: CustVendTable = true ? CustVendTable.data(VendTable::find(...)) : CustVendTable.data(CustTable::find(...)); X++: // перебор записей в DataSource формы for (lookupJournalTable = (dataSource && dataSource.getFirst(1) ? journalTable.data(dataSource.getFirst(1)) : journalTable); lookupJournalTable; lookupJournalTable = (dataSource ? dataSource.getNext() : null)) { ... } Тут следует заметить, что явное преобразование типов требуется только в том случае, если вычисляемые типы тринарного оператора имеют разное значение. Например, вот такой код будет откомпилирован без ошибок. X++: CustVendTable = true ? VendTable::find(...) : VendTable::find(...);
__________________
- Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... |
|
|
За это сообщение автора поблагодарили: Ruff (2), S.Kuskov (2). |
20.12.2020, 22:08 | #6 |
Участник
|
up-ну тему.
Стали причесывать код. Там где раньше все нормально было, на тернарных операторах стало выдавать "operand types are not compatible with the operator" или "Типы операндов не совместимы с оператором." Достает. Можно как-то это убрать ? Погуглил Преобразование System.String в str https://community.dynamics.com/ax/f/...patible/577041 https://community2.dynamics-int.com/...tor-in-ax-2012 http://www.javaear.com/question/28894455.html Ясности не добавило. Мне кажется это явно какой-то баг компилятора. Так как аналогичный код написаный через if и оператор присваивания работает хорошо и ругани нет. Для примера вот такой код из реального проекта X++: public static boolean validateField_MRC(Common _record, FieldId _fieldId, boolean _ignoreDataSourceLevel = false) { DictField dictField; FormDataSource fds; FormDataObject fdo; str testStr; container con; boolean ret; ; dictField = new DictField(_record.TableId, _fieldId); fds = null; // не ругается fds = !_ignoreDataSourceLevel ? (SysDictClass::as(_record.dataSource(), classNum(FormObjectSet))) : null; // не ругается fds = !_ignoreDataSourceLevel ? (SysDictClass::as(_record.dataSource(), classNum(Object))) : null; // не ругается fds = !_ignoreDataSourceLevel ? _record.dataSource() : null; // ругается fdo = fds ? fds.object(_fieldId) : null; ret = !(dictField.mandatory() || (fdo && fdo.mandatory())) || validateFieldIsSet_MRC(_record, _fieldId); if (ret) { ret = fdo ? fdo.validate() : _record.validateField(_fieldId); // ругается - один boolean отличается от другого ? ret = fdo ? fdo.validate() : any2int(_record.validateField(_fieldId)); // не ругается ret = fdo ? fdo.validate() : any2enum(_record.validateField(_fieldId)); // не ругается ret = fdo.validate(); // не ругается ret = _record.validateField(_fieldId); // не ругается } testStr = conPeek(con, 1) ? conPeek(con, 2) : @""; // ругается testStr = conPeek(con, 2); // не ругается testStr = @""; // не ругается return ret; } 2. Какие идеи почему так сделано ? Мне пока кажется что это из-за проблем с маршаллингом X++ <---> CIL Самый простой способ, какой нашел - юзать any2XXX() функции - так читаемость кода страдает меньше всего. Либо переделывать на if else c явным присваиванием. Для случая классов и таблиц можно написать в Global свои методы any2Object_MRC() и any2Common_MRC(). Можно также задействовать SysDictClass::as() X++: // см. также Global::asObject() static public Object any2Object_MRC(anytype _parm) { Object ret; ; switch (typeOf(_parm)) { case Types::Class: // info(" any2Object_MRC:Class"); return _parm; // break; case 44: // typeOf(null) // info(" any2Object_MRC:null of Class"); return _parm; // break; case Types::AnyType: // такое бывает когда дефолтное значение anyType приходит в CIL // info(" any2Object_MRC:anyType"); return _parm; // break; default: // info(" any2Object_MRC:other"); } return ret; } X++: // см. также Global::asCommon() // и SysDictTable::as() // static public Common any2Common_MRC(anyType _parm) { ; return _parm; } Последний раз редактировалось Logger; 20.12.2020 в 22:19. |
|
20.12.2020, 22:26 | #7 |
Участник
|
Добавлю еще 3-й вопрос.
почему такой код X++: fds = !_ignoreDataSourceLevel ? _record.dataSource() : null; // ругается X++: fds = null; // не ругается X++: ret = fdo ? fdo.validate() : _record.validateField(_fieldId); // ругается - один boolean отличается от другого ? а вот так нормально X++: ret = fdo ? fdo.validate() : any2int(_record.validateField(_fieldId)); // не ругается ret = fdo ? fdo.validate() : any2enum(_record.validateField(_fieldId)); // не ругается Последний раз редактировалось Logger; 20.12.2020 в 22:50. |
|
20.12.2020, 22:31 | #8 |
Участник
|
4-й вопрос. - Кто как обходит такое поведение компилятора?
пока вижу такие способы а. Отказаться от тернарного оператора в случае когда выдает ругань. б. Использовать any2XXX функции, дописав недостающие самим в global классе. Есть ли в этом какой то риск в случае CIL ? в. Забить на предупреждения компилятора (а как их тогда подавить корректно ? Грубые хаки в виде кода на insert / write методах таблицы TmpCompilerOutput c запретом вставки записи - я не считаю. Это крайний вариант) г. ... 5-й вопрос - как с этим обстоит дело в 365-й ? Последний раз редактировалось Logger; 20.12.2020 в 22:51. |
|
20.12.2020, 22:39 | #9 |
Участник
|
Цитата:
Так как при таком способе 1. теряется курсор к БД, а он бывает нужен. 2. теряется orig значение буфера. 3. buffer.data() глючно возвращает дубликат если buffer наследуемая табличка - не все поля заполняет. Возможно и при передаче буфера по ссылке внутрь вызова Data() тоже может быть фигня. см. тему Приведение типов для таблиц ax2012 (там описана проблема для orig() но у data() те же проблемы) в общем мне кажется что метод any2common_MRC() безопаснее и удобнее. Если нужно тип явно сохранить то можно задействовать SysDictTable::as вместо global::any2common_MRC Приведение типов для таблиц ax2012 Последний раз редактировалось Logger; 20.12.2020 в 22:48. |
|
21.12.2020, 04:18 | #10 |
Участник
|
Цитата:
https://ru.wikipedia.org/wiki/%D0%9A...D%D0%B8%D0%B5) в java изначально типы ковариантны. и дополнительно было очень много послаблений в примитивных типах. в аксапте изначально добавили ковариантность в методы классов. что позволяло до ax2009 указывать производные типы методах классов наследников (уж не знаю по недосмотру или был какой замысел). в ax2012 с какого-то перепуга разработчики сделали типы инвариантными как в C# 2.0. причем очень жестко. из-за этого нельзя уточнять тип в параметрах методов и в возвращаемых значениях. в качестве побочного эффекта получили вот такие затыки в тренарных операторах, а также в map (который AOT). именно из-за этого лично я считаю ax2012 худшей аксаптой изо всех сделанных. были слухи, что в ax2012 делали перегрузку методов и генерики. но ни перегрузки, ни генериков в аксапту так и не завезли. осталась только возможность писать в коде генерик типы .net (аксаптовские типа в таких конструкциях писать нельзя) System.Array<System.Object> arr; но зато в ax2012 ввели оператор языка is и as. в d365fo, насколько я помню, типы снова стали ковариантными. после того, как в C# 4.0+ добавили ковариантность для генериков это фича. Цитата:
if - это инструкция (statement), которая состоит из нескольких выражений тренарный - это одно выражение (expression) сделано "как в c#" людьми, которые кроме c# ничего не знают. если сформулировать утверждение полностью, то сразу станет понятно. достаточно дописать версию "из-за проблем с маршаллингом X++ <---> .net 3.5-" Цитата:
и не надо использовать эти угрёбищные суффиксы! пожалуйста. Цитата:
Христа ради! Цитата:
4. странные и мигающие глюки с map'ами Господи! Только не в global... там и так уже насрато... Последний раз редактировалось mazzy; 21.12.2020 в 04:54. |
|
|
За это сообщение автора поблагодарили: Logger (5). |
21.12.2020, 08:30 | #11 |
Участник
|
Спасибо. Интересно.
Но у меня вопрос практический. Как правильнее подавить предупреждения компилятора. Какие при этом риски. Не вылезет ли что нить притисполнении под cil. На первый взгляд, сюрпризов не должно быть. Функции any2xxx должны решить вопрос. |
|
21.12.2020, 08:48 | #12 |
Участник
|
Цитата:
только методы Any::toXXX() пожалуйста. |
|
23.12.2020, 14:34 | #13 |
Участник
|
Цитата:
Я даже в .net framework не нашёл такую generic-конструкцию для System.Array
__________________
Дмитрий |
|
23.12.2020, 16:58 | #14 |
Участник
|
|
|
|
За это сообщение автора поблагодарили: Logger (3). |
23.12.2020, 23:09 | #15 |
Участник
|
Гуглим:
Ковариа́нтность и контравариа́нтность[1] в программировании — способы переноса наследования типов на производные[2] от них типы — контейнеры, обобщённые типы, делегаты и т. п. Термины произошли от аналогичных понятий теории категорий «ковариантный» и «контравариантный функтор». Цитата:
Цитата:
Вот хочется проверить. У кого под руками есть 2012, можете проверить, что поддерживается именно ковариантность а не произвольное переопределение. Т.е. что контроллируется что метод производного класса обязан возвращать именно подкласс результата переопределенного метода, а не вообще все, что угодно, лишь бы оно было классом (назовем это пофиг-вариантностью) Цитата:
в ax2012 с какого-то перепуга разработчики сделали типы инвариантными как в C# 2.0. причем очень жестко.
из-за этого нельзя уточнять тип в параметрах методов и в возвращаемых значениях. X++ изначально это статически типизированный язык натянутый на не очень строго типизированный рантайм. MyClass x = otherValue; x.myMethod(a); Будет работать всегда, когда у otherValue есть метод совпадающий по имени и имеющий один обязательный параметр. Не важно, otherValue наследуется от MyClass или нет. JFYI, Параметры методов, наоборот, должны быть контровариантными см. LSP. Цитата:
были слухи, что в ax2012 делали перегрузку методов и генерики.
но ни перегрузки, ни генериков в аксапту так и не завезли. На уровне IL опциональные параметры компилируются в перегрузки. Цитата:
в d365fo, насколько я помню, типы снова стали ковариантными.
после того, как в C# 4.0+ добавили ковариантность для генериков Цитата:
сделано "как в c#" людьми, которые кроме c# ничего не знают.
Последний раз редактировалось belugin; 23.12.2020 в 23:21. |
|
28.12.2020, 13:58 | #16 |
Участник
|
вот и меня настигло:
X++: Common translation;
FormObjectSet translationDS = translation.isFormDataSource() ? translation.dataSource() : null; |
|
28.12.2020, 14:12 | #17 |
Участник
|
Цитата:
проверил. не работает. был же сарайчик... значит, я путаю. Цитата:
их ввели где-то в 6ой версии. ввели сразу ковариантные. Цитата:
Цитата:
конечно. это всего лишь мое личное мнение. |
|
28.12.2020, 22:08 | #18 |
Участник
|
Цитата:
Цитата:
в ax2012 при компиляции тип должен сопадать точно. что в параметрах, что в типе возвращаемого значения.
|
|
28.12.2020, 23:27 | #19 |
Участник
|
Эммм... Макс, если честно, то я совсем запутался. А раскручиывать цепочку цитат и контр-вопросов совсем нет никакого желания.
Но совершенно очевидно, что у тебя есть особое мнение по этому поводу. Также несомненно, что я могу дико ошибаться. вот исходный вопрос: Цитата:
Чтобы было полезно и познавательно читателям аксфорума можешь (без оглядки на мои ответы) сформулировать в одном посте: 1. что можно сделать с подобными тренарными операторами в ax2012? (примеры рассыпаны выше по ветке) 2. почему и зачем это возникло в ax2012 на твой взгляд? понятно, что для совместимости в CIL. А в CIL это зачем и почему раньше этого в X++ не было? 3. какова ситуация с тренарными операторами в D365FO и почему так случилось? 4. а также любые твои мысли на тему ветки - будет интересно. Последний раз редактировалось mazzy; 28.12.2020 в 23:36. |
|
29.12.2020, 07:27 | #20 |
Участник
|
Тернарными операторами.
Прошу прощения что поправляю.
__________________
Дмитрий |
|
|
За это сообщение автора поблагодарили: mazzy (2). |
Теги |
ax2012, ax2012r3, тернарный оператор |
|
|