Code style, использовать ли переменные?
// неужели не понятно, что в разных случаях получается по разному?
неужели не понятно, что в разных случаях получается по разному?Ну вот и хочется узнать конкретнее об этих случаях.
Тут есть мнение, что имя временной переменной служит как бы комментарием к тому, что в ней лежит - для случаев, когда это непонятно из контекста. Но, как мне кажется, из контекста (и из имён аргументов других методов) это будет понятно практически всегда.
TypeOfX
чё за херь?
Не бойся, я не имел в виду макросы. Просто имя класса, такое же, как SomeClass.

да, первый вариант предпочтительнее. Второй вариант считаю плохим стилем, он может быть оправдан только, если производился формальный рефакторинг. Кстати, ReSharper делает преобразования в обе стороны от одной записи к другой.
static SomeType DoSomething(SomeOtherType arg) {да, это ты увлекся, это уже ковнокод получаетca — вводится лишняя сущность — метод Tee.
return new SomeClass(arg.something.PassDebug.DoSomething.Tee(obj => obj.UpdateSomething.something;
}
тогда, на мой взгляд, вот это SomeFunc(x => x.DoSomething лучше. В этой записи не упоминается имя класса, оно выводится автоматически.Это - плюс. А вот все бесконечные x => x.*** - имхо, минус.
да, первый вариант предпочтительнее. Второй вариант считаю плохим стилемМожно поконкретнее?
x => x.*** - имхо, минус.а что тут лишнего? это "x => x."? да, возможно, и можно было бы сократить до ".", но, имхо, это не особо принципиально, привыкаешь. Когда лишними являются четко повторяющиеся конструкции, то к такому привыкаешь и глаз уже пропускает такое. А вот, если нужно писать и читать имя класса это уже действительно лишняя инфа и поэтому напрягает.
Чтобы к TResult f(T1 arg1, T2 arg2, T3 arg3) можно было обратиться и как к Func<T1, TResult> f(T2 arg2, T3 arg3 и как к Func<T1, T2, TResult> f(T3 arg3) (Func<T1, T2, T3, TResult> f уже есть, это просто f без скобок); ну и то соответствие между динамическими и статическими методами, о котором я уже сказал (т.е. чтобы можно было в любом классе (необязательно статическом) можно было написать статический extension для данного класса метод).
Первое решается и руками, но это задолбаешься для каждого количества аргументов писать по 2*n extension-методов curry (для func и для action)... да и явно типизированные к своим типам (ну вроде как Predicat<T> вместо Func<T, bool>) делегаты придётся явно кастовать в Func-и и Action-ы...
Когда лишними являются четко повторяющиеся конструкцииНу так они не чётко повторяются, ты не можешь написать x => x => x.DoSomething(x надо писать x => y => y.DoSomething(x).
О, я понял, чего мне не хватает.Смени язык на тот, что поддерживает карринг. А также где можно определять control flow операторы типа |> и т.п. F# тот же.
поэтому такая ботва
return new SomeClass(arg.something.DoSomething.something;
идет лесом
а такую
var x = arg.something;
var y = x.DoSomething;
var z = new SomeClass(y);
var u = z.something;
return u;
можно свернуть до такой
var y = arg.something.DoSomething;
var z = new SomeClass(y);
return z.something;
Ну так они не чётко повторяются, ты не можешь написать x => x => x.DoSomething(x надо писать x => y => y.DoSomething(x).да, автоматический карринг не помешал бы. Может и введут когда-нибудь.
имхо надо чтобы весь код нормально читался либо сверху вниз, либо слева направо.чё та я не проникся глубоким смыслом этого правила. Я и первый вариант легко читаю слева на право.
ну как бы к объекту arg.something.DoSomething применяется сначала приведение (операция стоит от него слева а потом взятие поля - операция уже стоит справа от него
new SomeClass(
arg.something.DoSomething
).something;
т.е.выражения вида f(g(h(x принципиально плохо читаются?
Я бы не сказал, что они читаются сильно лучше, чем x.h.g.f
как раз нормально читается слева направо
а вот если так: f(g(h(x).s).y) то плохо
апд:второй вариант пенартура в однобуквенной буквенной нотации превращается вf(a.s.d.x
может это и нормально, но мне режет глаз
что они читаются сильно лучше, чем x.h.g.fда, но они позволяют вводить несколько аргументов у любой функции, и при это все аргументы симметричны, а как твою (extention) запись обобщить на случай многих аргументов?
а как твою (extention) запись обобщить на случай многих аргументов?В теории - { x, y }.f
Просто имеет смысл определиться, какую запись использовать - слева направо или справа налево. А сейчас приходится в части случаев писать x.f а в части - f(x).
а вот если так: f(g(h(x).s).y)Ты просто идеальную иллюстрация моего примера запостил: x.h.s.g.y.f.
{ x, y }.fтогда уж (x, y)f
но, на самом деле, справа налево это плохо читается — сначала идет формирование пары (x, y а потом мы видим зачем мы эту пару формируем (чтобы передать в функцию f). Классическая запись f(x, y) приятнее — сначала видно, что идет вызов функции f, затем идет формирование для нее аргументов.
сначала идет формирование пары (x, y а потом мы видим зачем мы эту пару формируем (чтобы передать в функцию f).Так же можно сказать и про обычное x.f
Речь не о том, что лучше, подход "слева направо" или подход "справа налево", а о том, что любой из них лучше подхода "в 50% случаев слева направо, а ещё в 50% - справа налево".
Так же можно сказать и про обычное x.fне скажи, здесь x.f у нас есть объект и мы для объекта вызываем метод. А вот если аргумента два, то мы формируем пару и потом вызываем функцию. Так вот хочется сначала видеть, зачем мы начинаем формировать пару. Т.е. в случае одного аргумента перестановка аргумента и имени функции не влияет на читаемость, а если число аргументов больше одного, влияет.
не скажи, здесь x.f у нас есть объект и мы для объекта вызываем метод. А вот если аргумента два, то мы формируем пару и потом вызываем функцию. Так вот хочется сначала видеть, зачем мы начинаем формировать пару.А если x заменяем на x.y?
Чем принципиально отличается x.y.f от {x, y}.f? Точно так же - "хочется сначала видеть, зачем мы начинаем вызывать метод y у x".
static TResult _<TResult>(this Func<TResult> func) {
return func;
}
static TResult _<T1, TResult>(this T1 arg1, Func<T1, TResult> func) {
return func(arg1)
}
...
и тогда можно будет вместо
{x, y}.f
писать
{x, y, f}._
Но это уже было
хочется сначала видеть, зачем мы начинаем вызывать метод y у xне знаю как ты, а я вызываю метод затем, что этого требует задача, которую я программирую. А вот, потребность формировать пару возникает из-за способа записи вызова функции с двумя аргументами. Формирование пары это фактически начало записи вызова функции, и, на мой взгляд, лучше сначала прочитать название функции
Запись f(g(h(x разворачивается в цепочку без вложенных скобочек x.f.g.h только при одном аргументе. Оптимально как раз смешивать право и левосторонние стили
— правосторонний стиль позволяет избавиться от вложенные скобочки для функций с одним аргументом, что является плюсом. Но имя функции стоит не на первом месте в записи вызова функции с несколькими аргументами, что является минусом.
— для левостороннего стиля ситуация обратная.
Запись вызова функции при правостороннем стиле
— для функций одного аргумента “.f”. Видим, что запись начинается почти с имени функции. Точка не мешает, поскольку это символ, который не зависит от вызываемой функции.
— для нескольких переменных “{..., ..., ..., ... ...}.f”. Тут уже в начале идет формирование набора аргументов, а мы еще не знаем, зачем мы формируем этот набор.
Тут уже в начале идет формирование набора аргументов, а мы еще не знаем, зачем мы формируем этот набор.Ещё раз. А если у нас в первом случае ("функция одного аргумента") этот "один аргумент" записывается не одной буквой, а тоже каким-то сложным выражением? Получается то же самое - формируем аргумент, а ещё не знаем, зачем.
var x = DoSomething(arg);
var y = f(x);
var z = g(x);
return h(y, z);
можно, ценой говнокода, обойтись без этой отдельной строчки:
return (x => h(f(x g(oSomething(arg;
Тут уже нихрена не понять, что происходит, но, если немного модифицировать (нынешний язык такого не позволяет то выйдет терпимо:
return arg.DoSomething.(x => h(f(x g(x;
или, после ещё одного преобразования:
return arg.DoSomething.(x => { f(x g(x) }).h;
или, совсем сильно фантазируя:
return { arg.DoSomething f, g }.Calculate.h;
(правда, хрен напишешь автокомплишен, который с таким безумием сможет работать, так что для ООП это, наверное, всё-таки неприменимо).
Получается то же самое - формируем аргумент, а ещё не знаем, зачем.а вот и не тоже самое, сложное выражение для аргумента формируется, исходя из поставленной задачи, а вот пару формируем из-за способа записи.
а вот пару формируем из-за способа записиЧем отличается {x, y} от x.f?
В лиспе вот, вроде бы, именно такая запись используется, и особо никто не жалуется.
Чем отличается {x, y} от x.f?тем, что вложенные скобочки {{x, y}, z} читаются хуже, чем x.y.z
В лиспе вот, вроде бы, именно такая запись используется, и особо никто не жалуется.возможно, у записи {{x, y}, z} есть плюсы, которые перекрывают указанный минус. Я пока не в курсе.
{x, y} от x.f?кстати, ты тут опечатался или нет?
кстати, ты тут опечатался или нет?Нет - имеются в виду не конкретные случаи, а вообще.
возможно, у записи {{x, y}, z} есть плюсы, которые перекрывают указанный минус. Я пока не в курсе.В лиспе афаик вместо фигурных скобочек - круглые, а запятых нет. Перекрывают?

UPD: Вру, в лиспе функция идёт в начале, а не в конце... хотя, если подумать - всё это условности, наверное, f(x) ничем не отличается от x(f). Какая разница, sqrt(4) или 4(sqrt)?
тем, что вложенные скобочки {{x, y}, z} читаются хуже, чем x.y.zЧем хуже?
Ну и не забывай, что в моей выдуманной нотации для функций с одним аргументом можно скобочки не писать, нужна в них возникает только при написании функций от нескольких аргументов.
Ср.В телепатическом режиме предполагаю, что лучшим не является "самый короткий код".
code:
static SomeType DoSomething(SomeOtherType arg) {
var x = arg.something;
var y = x.DoSomething;
var z = new SomeClass(y);
var u = z.something;
return u;
}
vs
code:
static SomeType DoSomething(SomeOtherType arg) {
return new SomeClass(arg.something.DoSomething.something;
}
Какой вариант лучше (конечно, при условии, что все временные переменные используются по одному разу)?
Лучший вариант виден в конкретике. В каких-то случаях предпочтителен один вариант, а в каких-то другой. Включив воображение, я все же в 90% случаях выбрал бы первый вариант и обязательно дал бы осмысленные имена переменным вместо x, y, z. Тогда полученный код настроен на легкую модификацию и расширение в будущем.
Во втором варианте и дебажить можно:Так как код приходится менять, то второй вариант я бы назвал "недебугельным". Первый вариант гораздо (даже слишком) debugger-friendly.
я бы назвал "недебугельным".на самом деле, в 95-99% случаев под дебагом можно посмотреть столько же инфы и для второго способа записи
var a = xxx.DoSomething;
Debugger.Debug(a);
a.DoSomething;
и
xxx.DoSomething.PassDebug.DoSomething;
bool isCurrentDirectorySystem = (Enviroment.CurrentDirectory == Environment.SystemDirectory);
if (isCurrentDirectorySystem)
...
Я сейчас начинаю предпочитать второй вариант (если документировать переменными нет необходимости но немного измененный (верю и надеюсь, что он помогает дебагить - даёт доп. инфу о строке с исключением,- но не проверял).
В предельном случае будет как-то так:
static SomeType DoSomething(SomeOtherType arg)
{
return new SomeClass
(
arg
.something
.DoSomething
)
.something;
}
Оставить комментарий
kruzer25
Ср.vs
Какой вариант лучше (конечно, при условии, что все временные переменные используются по одному разу)?
Во втором варианте и дебажить можно:
и, в принципе, даже допускается совершение каких-то промежуточных действий с объектами (хотя и выходит говнокод):
И ещё пришло в голову - почему на уровне языка (C#) нет фичи, чтобы методы объекта являлись и методами класса, принимающими на вход объект? То есть, чтобы
работало, как
?
Тогда можно было бы вместо всяких SomeFunc(x => x.DoSomething писать просто SomeFunc(TypeOfX.DoSomething)
Хотя, выходит, даже лучше, чтобы статический метод выглядел как-то так:
и можно будет писать вместо SomeFunc(x => x.DoSomething('xxx' писать SomeFunc(TypeOfX.DoSomething('xxx'