Действительно ли это - злоупотребление попыткой/наконец?

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

Опция A: несколько возвратов, повторенный блок кода

public bool myMethod() {
    /* ... code ... */

    if(thisCondition) {
        /* ... code that must run at end of method ... */
        return false;
    }

    /* ... more code ... */

    if(thatCondition) {
        /* ... the SAME code that must run at end of method ... */
        return false;
    }

    /* ... even more code ... */

    /* ... the SAME CODE AGAIN that must run at end of method ... */
    return lastCondition;
}

Это заставляет меня чувствовать себя грязным, чтобы видеть, что тот же (небольшой) блок кода повторил три раза каждый раз возвраты метода. Кроме того, я хотел бы разъяснить что два return false операторы выше могут, конечно, быть описаны как возврат середины метода... они абсолютно не, "охраняют операторы".

Действительно ли Опция B немного более приемлема? Я чувствую, что могу, злоупотребляя попыткой/наконец, и я надеюсь, что существует что-то совершенно другое, которое я должен делать.

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

public bool myMethod() {
    try {
        /* ... code ... */

        if(thisCondition) {
            return false;
        }

        /* ... more code ... */

        if(thatCondition) {
            return false;
        }

        /* ... even more code ... */

        return lastCondition;
    } finally {
        /* ... code that must run at end of method ... */
    }
}

Наконец, Опция C является лучшим решением в моей книге, но моей команде не нравится этот подход по любой причине (причинам), следовательно я ищу компромисс.

Опция C: единственный возврат, условные блоки

public bool myMethod() {
    /* ... code ... */

    if(!thisCondition) {
        /* ... more code ... */
    }

    if(!thisCondition && !thatCondition) {
        /* ... even more code ... */
    }

    /* ... code that must run at end of method ... */
    return summaryCondition;
}

Если Вы хотите обсудить несколько операторов возврата, сделайте так в этом вопросе.

32
задан Community 23 May 2017 в 12:05
поделиться

11 ответов

Исключения должны быть исключительными, поэтому мне не нравится вариант B, если нет других исключений (Примечание для тех, кто проголосовал против - я не говорю, что иметь finally неверно, просто я предпочитаю не иметь его, если нет исключений - если у вас есть причины, прокомментируйте)

Если код нужен всегда, как насчет рефакторинга на 2 функции

public bool myMethod() {
    bool summaryCondition = myMethodWork();
    // do common code
    return summaryCondition;
}

private bool myMethodWork() {
   /* ... code ... */

    if(thisCondition) {
        return false;
    }

    /* ... more code ... */

    if(thatCondition) {
        return false;
    }

    /* ... even more code ... */

    return lastCondition;
}
28
ответ дан 27 November 2019 в 20:08
поделиться

Если код должен выполняться даже тогда, когда какой-либо другой код вызывает исключение, то блок finally является правильным решением.

Если он не должен запускаться в случае исключения (т.е. он необходим только для «нормального» возврата), то использование finally будет означать злоупотребление этой функцией.

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

Когда этот код оказывается слишком сложным (а это вполне реальная возможность), то пора реорганизовать метод, извлекая один или несколько методов.

Простейший рефакторинг выглядит примерно так:

public boolean  myMethod() {
    boolean result = myExtractedMethod();
    /* ... code that must run at end of method ... */
    return result;
}

protected boolean myExtractedMethod() {
    /* ... code ... */

    if(thisCondition) {
        return false;
    }

    /* ... more code ... */

    if(thatCondition) {
        return false;
    }

    /* ... even more code ... */
    return lastCondition;
}
30
ответ дан 27 November 2019 в 20:08
поделиться

Это идеальное место для GOTO

* утки *

15
ответ дан 27 November 2019 в 20:08
поделиться

Это длинный указатель на постоянную широкую последовательность (т.е. последовательность из широких символов).

Так как это широкий ряд, вы хотите, чтобы ваша постоянная выглядит как: L «StartWindow» . Я бы тоже не стал создавать промежуточный a , я бы просто передал L «StartWindow» для параметра:

ghTest = FindWindowEx(NULL, NULL, NULL, L"TestWindow");

Если вы хотите быть педантично правильным, «LPCTSTR» является «текстовой» последовательностью - широкой последовательностью в построении Юникода и узкой последовательностью в построении ANSI, поэтому вы должны использовать соответствующий макрос:

ghTest = FindWindow(NULL, NULL, NULL, _T("TestWindow"));

Однако мало кто заботится о создании кода, который можно скомпилировать как для наборов символов Юникода, так и для наборов символов ANSI, и если вы не добьетесь того, чтобы он действительно работал правильно, это может быть немного дополнительной работой для небольшой выгоды. В этом конкретном случае не так много дополнительной работы, но если вы манипулируете последовательностями, есть полный набор макросов манипуляции последовательностями, которые разрешаются в правильные функции.

-121--848988-

Вы можете использовать pulldom API для обработки анализа большого файла, не загружая его в память одновременно. Это обеспечивает более удобный интерфейс, чем использование SAX с незначительной потерей производительности.

Он в основном позволяет передавать XML-файл до тех пор, пока вы не найдете интересующий вас бит, а затем начать использовать регулярные операции DOM после этого.


from xml.dom import pulldom

# http://mail.python.org/pipermail/xml-sig/2005-March/011022.html
def getInnerText(oNode):
    rc = ""
    nodelist = oNode.childNodes
    for node in nodelist:
        if node.nodeType == node.TEXT_NODE:
            rc = rc + node.data
        elif node.nodeType==node.ELEMENT_NODE:
            rc = rc + getInnerText(node)   # recursive !!!
        elif node.nodeType==node.CDATA_SECTION_NODE:
            rc = rc + node.data
        else:
            # node.nodeType: PROCESSING_INSTRUCTION_NODE, COMMENT_NODE, DOCUMENT_NODE, NOTATION_NODE and so on
           pass
    return rc


# xml_file is either a filename or a file
stream = pulldom.parse(xml_file) 
for event, node in stream:
    if event == "START_ELEMENT" and node.nodeName == "AssetType":
        if node.getAttribute("longname") == "characters":
            stream.expandNode(node) # node now contains a mini-dom tree
            type_nodes = node.getElementsByTagName('type')
            for type_node in type_nodes:
                # type_text will have the value of what's inside the type text
                type_text = getInnerText(type_node)

-121--4667595-

Использование try/finally для управления потоком кажется мне похожим на GOTO.

0
ответ дан 27 November 2019 в 20:08
поделиться

Не злоупотребляйте try / finally, если вам не нужно вырваться из внутренних циклов. Злоупотребления делаю / пока.

bool result = false;
do {
  // Code
  if (condition1) break;
  // Code
  if (condition2) break;
  // . . .
  result = lastCondition
} while (false);
2
ответ дан 27 November 2019 в 20:08
поделиться

Если код должен выполняться даже при наличии исключения, то finally - не просто хороший выбор, это обязательное условие. Если это не так, то finally не нужен. Похоже, что вы хотите найти формат, который "выглядит" лучше всего. Но на кону стоит нечто большее.

3
ответ дан 27 November 2019 в 20:08
поделиться

Во-первых, есть несколько вещей, которые вы должны рассмотреть. Вы должны иметь дело с летним временем, которое время от времени, кажется, меняется (даты начала и окончания менялись дважды за последние 10 лет). Так в Северном полушарии зима, восточное время -5 GMT (или UTC). Но летом это -6 GMT или это -4 GMT, я никогда не могу держать его прямо (и не должен).

Существуют некоторые функции библиотеки DNF для обработки информации о часовых поясах, однако для наиболее полезных вещей вам действительно требуется .net 3.5. В .net 3.5 есть класс TimeZoneInfo.

TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime dt = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.Now,
     TimeZoneInfo.IsDaylightSavingsTime(tzi) ? 
        tzi.DaylightName : tzi.StandardName);
if (dt.Hour == 17)
    ....

Также имейте в виду, что два раза в год каждый час теряется или набирается, поэтому вы также должны учитывать это, если, например, у вас есть таймер обратного отсчета, вы отображаете «время до следующей обработки» или что-то подобное. Дело в том, что обработка времени не так проста, как казалось бы сначала, и есть много крайних случаев.

-121--4631788-

Нет надежды получить какую-либо полезную информацию из файла .suo. Даже если вам удастся реконструировать его (сложный) формат, ваша напряженная работа сойдет на нет со следующим выпуском или пакетом обновления для Visual Studio.

Файл сохраняет состояние IDE. Это состояние также доступно из интерфейсов расширяемости. Используйте макросы для продвижения вперед. Для начала выполните поиск пространства имен EnvDTE в библиотеке MSDN.

-121--3414197-

Ваше решение опции C недалеко от оптимального, поскольку оно адекватно кодирует правильную последовательность выполнения, которую вы пытаетесь выполнить.

Аналогично, использование вложенных операторов if делает то же самое. Это может быть визуально менее привлекательно, но это проще понять, и делает поток выполнения довольно очевидным:

public bool myMethod() { 
    boolean  rc = lastCondition; 

    /* ... code-1 ... */ 

    if (thisCondition) { 
        rc = false;
    } 
    else {  
        /* ... code-2 ... */ 

        if (thatCondition) { 
            rc = false;
        } 
        else {  
            /* ... code-3 ... */ 
            rc = ???;
        }  
    }

    /* ... the code that must run at end of method ... */ 
    return rc;  
}

Упрощение кода дает:

public bool myMethod() { 
    boolean  rc = false; 

    /* ... code-1 ... */ 

    if (!thisCondition) { 
        /* ... code-2 ... */ 

        if (!thatCondition) { 
            /* ... code-3 ... */ 
            rc = lastCondition;
        }  
    }

    /* ... the code that must run at end of method ... */ 
    return rc;  
}

Упрощенный код также показывает, чего вы на самом деле пытаетесь достичь: вы используете условия теста, чтобы избежать выполнения кода, поэтому, вероятно, вы должны выполнять этот код, когда условия false вместо того, чтобы делать что-то, когда они true .

Чтобы ответить на ваш вопрос о блоках try-finally : Да, вы можете злоупотреблять ими. Ваш пример недостаточно сложен, чтобы оправдать использование try-final. Но если бы это было сложнее, это могло бы быть.

См. мое мнение по адресу: Перейти к заявлению, считающемуся вредным: ретроспектива , «Обработка исключений» .

3
ответ дан 27 November 2019 в 20:08
поделиться

ИМО идея состоит в том, чтобы поместить блок try на небольшую часть кода (например, вызов метода), который может вызвать известное исключение (например, чтение из файла, чтение int в Строку ). Таким образом, размещение блока try по всему коду метода на самом деле не лучший вариант, если только вы не ожидаете, что каждый код условия if потенциально вызовет один и тот же набор исключения. Я не вижу смысла использовать блок try только ради использования finally .

Если я не ошибаюсь, размещение больших кусков кода внутри try также делает его намного медленнее, но не уверен, так ли это в последних версиях Java.

Лично я бы выбрал вариант C. Но я тоже не имею ничего против варианта А.

0
ответ дан 27 November 2019 в 20:08
поделиться

Если код, который должен запускаться в конце метода, не использует локальные переменные метода, вы можете извлечь его в такой метод, как:

public boolean myMethod() {
    /* ... code ... */

    if(thisCondition) {
        return myMethodCleanup(false);
    }

    /* ... more code ... */

    if(thatCondition) {
        return myMethodCleanup(false);
    }

    /* ... even more code ... */

    return myMethodCleanup(lastCondition);
}

private boolean myMethodCleanup(boolean result) {

    /* ... the CODE that must run at end of method ... */
    return result;
}

это все еще не Выглядит не очень красиво, но это лучше, чем использование конструкций типа goto. Чтобы убедить своих товарищей по команде, что решение с 1 возвратом может быть не так уж и плохо, вы также можете представить версию, используя 2 do {...} while (false); и break (* злая ухмылка *.)

0
ответ дан 27 November 2019 в 20:08
поделиться

Как насчет того, чтобы разбить его еще немного, чтобы дать что-то большее (извините, что я не использовал логические операторы Java в некоторое время ) вот так:

public bool findFirstCondition()
{
   // do some stuff giving the return value of the original "thisCondition".
}

public bool findSecondCondition()
{
   // do some stuff giving the return value of the original "thatCondition".
}

public bool findLastCondition()
{
   // do some stuff giving the return value of the original "lastCondition".
}

private void cleanUp() 
{
   // perform common cleanup tasks.
}


public bool myMethod() 
{ 


   bool returnval = true;
   returnval = returnval && findFirstCondition();
   returnval = returnval && findSecondCondition();

   returnval = returnval && findLastCondition();
   cleanUp();
   return returnval; 
}
2
ответ дан 27 November 2019 в 20:08
поделиться

Есть ли причина, по которой вы не можете просто сохранить возвращаемое значение и выйти из if?

   bool retVal = true;
   if (retVal && thisCondition) {
   }

   /* more code */

   if ( retVal ) {
     /* ... code that must run at end of method, maybe inside an if or maybe not... */

   }
   return retVal;
0
ответ дан 27 November 2019 в 20:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: