с помощью урожая в C# как я был бы в Ruby

Помимо просто использования yield для итераторов в Ruby я также использую его для передачи управления кратко назад вызывающей стороне перед возобновляющимся управлением в вызываемом методе. То, что я хочу сделать в C#, подобно. В тестовом классе я хочу получить экземпляр соединения, создать другой переменный экземпляр, который использует то соединение, затем передайте переменную вызывающему методу, таким образом, это может играться с. Я затем хочу, чтобы управление возвратилось к вызываемому методу так, чтобы соединение могло быть расположено. Я предполагаю, что желаю блок/закрытие как в Ruby. Вот общее представление:

private static MyThing getThing()
{
    using (var connection = new Connection())
    {
        yield return new MyThing(connection);
    }
}

[TestMethod]
public void MyTest1()
{
    // call getThing(), use yielded MyThing, control returns to getThing()
    // for disposal
}

[TestMethod]
public void MyTest2()
{
    // call getThing(), use yielded MyThing, control returns to getThing()
    // for disposal
}

...

Это не работает в C#; ReSharper говорит мне что тело getThing не может быть блок итератора потому что MyThing не тип интерфейса итератора. Это определенно верно, но я не хочу выполнять итерации через некоторый список. Я предполагаю, что не должен использовать yield если я не работаю с итераторами. Любая идея, как я могу достигнуть этой вещи блока/закрытия в C#, таким образом, я не должен переносить свой код в MyTest1, MyTest2... с кодом в getThing()тело?

12
задан niton 21 April 2015 в 12:28
поделиться

5 ответов

Что вам нужно, так это лямбда-выражения, что-то вроде:

// not named GetThing because it doesn't return anything
private static void Thing(Action<MyThing> thing)
{
    using (var connection = new Connection())
    {
        thing(new MyThing(connection));
    }
}

// ...
// you call it like this
Thing(t=>{
  t.Read();
  t.Sing();
  t.Laugh();
});

Это фиксирует t так же, как yield в Ruby. В C# yield отличается, он строит генераторы, которые можно итерировать.

19
ответ дан 2 December 2019 в 05:40
поделиться

Я мог бы передать делегата итератору.

delegate void Action(MyThing myThing);
private static void forEachThing(Action action) 
{ 
    using (var connection = new Connection()) 
    { 
        action(new MyThing(connection));
    } 
}
1
ответ дан 2 December 2019 в 05:40
поделиться

Можно поставить GetThing взять делегат, содержащий код, для выполнения, а затем передать анонимные методы из других функций.

0
ответ дан 2 December 2019 в 05:40
поделиться

yield в C# предназначен специально для возврата битов итерируемой коллекции. В частности, ваша функция должна возвращать IEnumerable или IEnumerable, чтобы yield работал, и он предназначен для использования внутри цикла foreach. Это очень специфическая конструкция в c#, и она не может быть использована так, как вы пытаетесь.

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

0
ответ дан 2 December 2019 в 05:40
поделиться

Вы говорите, что хотите использовать ключевое слово C # yield так же, как вы использовали бы ключевое слово Ruby yield . Вы, кажется, немного запутались в том, что эти двое на самом деле делают: у них нет абсолютно ничего общего друг с другом, то, о чем вы просите, просто невозможно.

Ключевое слово C # yield - это , а не эквивалент C # ключевого слова yield в Ruby . Фактически, не существует эквивалента ключевого слова Ruby yield в C #. И Ruby-эквивалент ключевого слова yield в C # - это , а не ключевое слово yield , это метод Enumerator :: Yielder # yield (также имеет псевдоним Enumerator :: Yielder # << ).

IOW, это для возврата следующего элемента итератора.Вот сокращенный пример из официальной документации MSDN:

public static IEnumerable Power(int number, int exponent) {
    var counter = 0;
    var result = 1;
    while (counter++ < exponent) {
        result *= number;
        yield return result; }}

Используйте его так:

foreach (int i in Power(2, 8)) { Console.Write("{0} ", i); }

Ruby-эквивалент будет примерно таким:

def power(number, exponent)
  Enumerator.new do |yielder|
    result = 1
    1.upto(exponent-1) { yielder.yield result *= number } end end

puts power(2, 8).to_a

В C # yield используется для получения значение для вызывающего , а в Ruby yield используется для передачи элемента управления аргументу блока

Фактически, в Ruby , yield - это просто ярлык для Proc # call .

Представьте, если бы yield не существовало. Как бы вы написали метод if на Ruby? Это будет выглядеть так:

class TrueClass
  def if(code)
    code.call
  end
end

class FalseClass
  def if(_); end
end

true.if(lambda { puts "It's true!" })

Это довольно громоздко. В Ruby 1.9 мы получили литералы процедур и сокращенный синтаксис для Proc # call , что делает его немного лучше:

class TrueClass
  def if(code)
    code.()
  end
end

true.if(->{ puts "It's true!' })

Однако Юкихиро Мацумото заметил, что подавляющее большинство процедуры высшего порядка принимают только один аргумент процедуры. (Тем более, что Ruby имеет несколько встроенных в язык конструкций потока управления, которые в противном случае потребовали бы нескольких аргументов процедуры, например if-then-else , которое потребовало бы два, и case-when , которые потребует n аргументов.) Итак, он создал специальный способ передать ровно один процедурный аргумент : блок. (Фактически, мы уже видели пример этого в самом начале, потому что Kernel # lambda на самом деле является обычным методом, который принимает блок и возвращает Proc .)

class TrueClass
  def if(&code)
    code.()
  end
end

true.if { puts "It's true!" }

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

def if
  ???.() # But what do we put here? We don't have a name to call #call on!
end

Однако, поскольку мы больше не есть имя, на которое мы можем отправлять сообщения, нам нужен другой способ. И снова мы получаем одно из тех решений 80/20, которые так типичны для Ruby: существует тонн вещей, которые можно было бы сделать с блоком: преобразовать его, сохранить в атрибуте, передать его в другой метод, изучите, распечатайте… Однако, по далеко , наиболее распространенным является его вызов. Итак, matz добавил еще один специальный сокращенный синтаксис именно для этого распространенного случая: yield означает « call блок, который был передан методу». Следовательно, нам не нужно имя:

def if; yield end

Итак, что C # эквивалентно ключевому слову Ruby yield ? Что ж, давайте вернемся к первому примеру Ruby, где мы явно передали процедуру в качестве аргумента:

def foo(bar)
  bar.('StackOverflow')
end

foo ->name { puts "Higher-order Hello World from #{name}!" }

Эквивалент C # точно то же самое:

void Foo(Action<string> bar) => bar("StackOverflow")

Foo(name => { Console.WriteLine("Higher-order Hello World from {0]!", name); })
7
ответ дан 2 December 2019 в 05:40
поделиться
Другие вопросы по тегам:

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