Возможно ли иметь Func <bool>в качестве условия - PullRequest
0 голосов
/ 16 декабря 2011

Добрый вечер,

Мне было интересно, смогу ли я сделать что-то вроде:

while(true)
{
   MyEnum currentValue = GetMyEnumValueFromDB();
   if(currentValue == MyEnum.BreakIfYouGetThis)
      break;
   else if(currentValue == MyEnum.AlsoBreakIfYouGetThis)
      break;
   else
      //Do some computing
}

Но вместо использования цикла while (true) я бы хотел инкапсулировать условное выражениелогику в Func и выполнить ее так:

while(new Func<bool>(/* what goes here? */))
{
   //Do some computing
}

По крайней мере, в моем случае это выглядело бы намного чище, но я не уверен, как это сделать (вроде как в Func / Action ..).

РЕДАКТИРОВАТЬ надеюсь, что это прояснит:
Это также можно сделать так:

while(GetMyEnumValueFromDB() != MyEnum.BreakIfYouGetThis && 
      GetMyEnumValueFromDB() != MyEnum.AlsoBreakIfYouGetThis)
{
   //Do some computing
}

Но это 2 обращения к базе данных ...

Спасибо =)

Ответы [ 4 ]

4 голосов
/ 16 декабря 2011

Ну, вы могли бы иметь:

Func<bool> condition = ...;

while (condition())
{
}

Это то, о чем ты думаешь? Это не совсем понятно ...

РЕДАКТИРОВАТЬ: В приведенном вами примере, я бы использовал что-то вроде:

private static readonly MyEnum[] BreakEnumValues = { 
    MyEnum.BreakIfYouGetThis,
    MyEnum.AlsoBreakIfYouGetThis
};

...

while (!BreakEnumValues.Contains(GetMyEnumValueFromDB()))
{
    ...
}

Или:

private static bool ShouldBreak(MyEnum enumFromDatabase)
{
    return enumFromDatabase == MyEnum.BreakIfYouGetThis ||
           enumFromDatabase == MyEnum.AlsoBreakIfYouGetThis;
}

...

while (!ShouldBreak(GetMyEnumValueFromDB))
{
    ...
}

РЕДАКТИРОВАТЬ: Чтобы противостоять ответу KeithS, это полностью верно:

while (new Func<bool>(() => {
    Console.WriteLine("Evaluating...");
    return true;
})()) {
    Console.WriteLine("In loop");
    count++;
    if (count == 5)
    {
        break;
    }
}

Это ужасно, но это действительно. (Это можно сделать чуть менее ужасным, явно вызвав Invoke, но это все же не очень приятно.)

2 голосов
/ 16 декабря 2011

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

while(KeepGoing())
{
    //Do stuff
}

bool KeepGoing()
{
    // Check conditions for continuing
    var value = GetMyEnumValueFromDB();
    return value != MyEnum.BreakIfYouGetThis && value != MyEnum.AlsoBreakIfYouGetThis;
}
0 голосов
/ 16 декабря 2011

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

//these won't compile; Func has no constructor
Func<bool> condition = new Func<bool>();

//As Jon Skeet notes, these will compile because of language-provided syntax,
//even though Funcs have no constructor with a delegate parameter.
Func<bool> condition = new Func<bool>(SomeNamedMethodReturnsBool);
Func<bool> condition = new Func<bool>(()=>true);

//and these will compile because you're directly assigning the delegate
Func<bool> condition = SomeNamedMethodReturnsBool;
Func<bool> condition = ()=>true;

Вы на самом деле не можете делать то, что вы хотите в C #; анонимный делегат должен быть назначен переменной или параметру для использования. Его нельзя использовать на месте в качестве «функционального литерала» в абстрактном смысле.

Даже если бы вы могли, полезность такого заявления ускользает от меня. Делегат не будет повторно создаваться каждый раз (компилятор превращает анонимных делегатов в именованные частные функции содержащего класса с именами гибридных приложений), но вы добавляете слой в стек вызовов только для оценки постоянного выражения, которое может быть помещается непосредственно в скобки оператора while (). Основная утилита анонимного делегата заключается в том, что вы можете поменять лямбды во время выполнения, и для этого вы ДОЛЖНЫ иметь переменную.

0 голосов
/ 16 декабря 2011

Во-первых, вызов базы данных в цикле while - плохая идея. Предварительно запросите то, что вам нужно, и выполните соответствующий цикл.

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

interface IWhileLoopContinuable
{
   bool KeepGoing {get;}
   void DoWork();
}

Тогда вы можете использовать TakeWhile для этого перечислимого:

foreach(var item in queriedAsIWhileLoopContinuable.TakeWhile(c => c.KeepGoing))
{
   item.DoWork();
}

При всем этом вы можете просто запросить то, что вы хотите, и поработать над этим.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...