C # либо вернуть false или ничего не делать - PullRequest
3 голосов
/ 25 января 2011

Мне интересно, есть ли способ не повторять одно и то же , если конструкция , а вместо этого вызывать StatusCheck () .Он не может вернуть истину, когда это удастся. Кто-нибудь знает лучшее название для этого вопроса?

bool Enable()
{
    if (!GetStatus(ref status)) { Trace.WriteLine("Error"); return false; }
    // do stuff

    if (!GetStatus(ref status)) { Trace.WriteLine("Error"); return false; }
    // do more stuff

    if (!GetStatus(ref status)) { Trace.WriteLine("Error"); return false; }
    // do even more stuff

    // 6 more times the above

    return true;
}

Ответы [ 11 ]

6 голосов
/ 25 января 2011

Вы можете создать метод CheckStatus(), который выдает исключение, если статус недопустим, а затем обработать это исключение в вашем методе Enable():

public void CheckStatus(int status)
{
    if (!IsValidStatus(status)) {
        throw new InvalidStatusException(status);
    }
}

public bool Enable()
{
    try {
        CheckStatus(status);
        // do stuff

        CheckStatus(status);
        // do more stuff

        CheckStatus(status);
        // do even more stuff

        // 6 more times the above

        return true;

    } catch (InvalidStatusException) {
        Trace.WriteLine("Error");
        return false;
    }
}
6 голосов
/ 25 января 2011

Я хотел бы, чтобы код, представленный комментариями "Делать вещи", был преобразован в методы. Вы вызываете эти методы и либо перехватываете исключения, либо проверяете возвращаемое значение этих методов (которое может быть текущим состоянием), а не повторно вызываете GetStatus().

Кроме того, я не понимаю, почему метод с именем GetStatus() будет иметь параметр ref, который, по-видимому, обновляется методом с текущим состоянием? Если вам нужен метод GetStatus(), я бы взял GetStatus() без аргументов и фактически вместо этого вернул бы текущий статус.

1010 * Т.е. *

Status status = GetStatus();

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

5 голосов
/ 25 января 2011

В зависимости от того, насколько серьезным вы хотите быть, вы можете создать какую-то очередь действий для того, чтобы разбить «do-stuff» в методах.

protected delegate void DoStuffDelegate(ref {TYPE} statusToCheck);

bool Enable()
{
  List<DoStuffDelegate> stuffToDo = new ...
  stuffToDo.Add(myFirstMethod);
  stuffToDo.Add(mySecondMethod);
  foreach(var myDelegate in stuffToDo)
  {
    if(!GetStatus(ref status)) { Trace.WriteLine("Error"); return false; }
    myDelegate(ref status);
  }
}

Как в хорошем, так и в плохом C # не допускаются никакие другие конструкции (например, определения препроцессора или такие, которые мы получили в C ++).

1 голос
/ 26 января 2011

Как насчет этого? Это требует минимальных изменений в вашем существующем коде. yield return твой друг; пусть компилятор сделает всю грязную работу:

IEnumerable<bool> StatusChecks()
{
    // Do stuff
    yield return GetStatus( ref status );
    // Do stuff
    yield return GetStatus( ref status );
    // Do stuff
    yield return GetStatus( ref status );
}

bool Enable()
{
    foreach ( var b in StatusChecks() )
    {
        if ( !b )
        {   
            Trace.WriteLine("Error");                       
            return false;
        } 
    }
    return true;
}

или, если вы не возражаете против LINQ-запроса с побочными эффектами, просто:

bool Enable()
{
     var result = StatusChecks().All( b => b );
     if ( !result )
     {
         Trace.WriteLine("Error");                       
     }
     return result;
}
1 голос
/ 26 января 2011

С LINQ вы можете сделать что-то вроде:

delegate bool StatusFunc( ref int status );

bool Enable()
{
    var funcs = new StatusFunc[] 
    {
        GetStatusA,
        GetStatusB,
        GetStatusC
    };
    return funcs.All( f => f( ref status ) );
}

Обратите внимание, что это немного хак, потому что у GetStatus функций есть побочные эффекты (изменение статуса), что нет в LINQ.-queries.

1 голос
/ 25 января 2011

Теперь вам не нужно использовать исключения, и все равно можно читать. Независимо от того, что вы все равно должны позвонить. Исключения составляют около 4000-8000 тактов.

bool Enable()
{
    if (GetStatus(ref status)) 
    { 
        // do stuff
        if (GetStatus(ref status)) 
        { 
            // do stuff
            if (GetStatus(ref status)) 
            { 
                // do stuff
                return true;
            }
        }
    }

    Trace.WriteLine("Error"); 
    return false;
}
1 голос
/ 25 января 2011

Возможное решение - создать оболочку «здравомыслие» для API, с которым вы работаете:

class Wrapper
{
    public void DoStuff() // Add static modifiers, parameters and return values as necessary
    {
        API.DoStuff();
        CheckStatus();
    }

    public void DoSomeOtherStuff()
    {
        API.DoSomeOtherStuff();
        CheckStatus();
    }

    private void CheckStatus()
    {
        Status status = default(Status);
        if(!GetStatus(ref status)) throw new InvalidStatusException();
    }
}

Немного работы, но это позволяет вашему клиентскому коду быть намного более читабельным:

bool Enable()
{
    try
    {
        Wrapper.DoStuff();
        Wrapper.DoSomeMoreStuff();
        return true;
    }
    catch(InvalidStatusException)
    {
        Trace.WriteLine("Error");
        return false;
    }
}
1 голос
/ 25 января 2011

Вы можете использовать исключения, и позволить исключению распространяться вверх.

void GetStatusAndException(ref Status) {
    if (!GetStatus(ref status))) Throw new Exception("Status exception");
}

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

bool GetStatusAndTrace(ref Status) {
    bool result = GetStatus(ref status))
    if (!result) Trace.WriteLine("Error");
    return result;
}

bool Enable()
{
    if (!GetStatusAndTrace(ref status))  return false; 
    // do stuff

    if (!GetStatusAndTrace(ref status))  return false; 
    // do more stuff

    if (!GetStatusAndTrace(ref status)) return false; 
    // do even more stuff

    // 6 more times the above

    return true;
}
0 голосов
/ 25 января 2011

Посмотрим, правильно ли я вас понял.

Вы можете использовать шаблон дизайна спецификации: http://en.wikipedia.org/wiki/Specification_pattern

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

Например, вы бы определили спецификацию «StatusSpecification», которая «делает некоторые вещи для проверки правильности состояния». Результат может быть логическим.

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

Подводя итог: вам нужно иметь некоторый «загрузчик спецификаций» и выполнять состояние объекта в соответствии с загруженными правилами для типа объекта. В конце концов, в этом случае вы собираетесь оценивать это состояние в одной строке (с точки зрения потребителя), и, возможно, логическим результатом может быть флаг вашего свойства.

0 голосов
/ 25 января 2011

Оберните код в блок try-catch и перепишите свой GetStatus (), чтобы вызвать исключение при ошибке.Если вы хотите сделать это по книге, реализуйте свое собственное исключение, наследуемое от System.Exception.Затем вы также можете добавить свойство «status» к исключению, если вам понадобится эта информация позже (однако, ваш пример кода не указывает на это).* Как я уже сказал, выброшенное и пойманное исключение действительно должно быть пользовательским исключением, но приведенное выше будет работать.

...