Вы говорите, что хотите использовать ключевое слово yield
в C # так же, как и ключевое слово yield
в Ruby. Вы, кажется, немного смущены тем, что на самом деле делают эти двое: оба абсолютно не имеют ничего общего с тем, о чем вы просите, просто не возможно.
Ключевое слово C # yield
является , а не эквивалентом C # ключевого слова Ruby yield
. На самом деле, не является эквивалентом ключевого слова 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
используется для выдачи control для аргумент блока
На самом деле, в 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: есть тонн вещей, которые можно сделать с блоком: преобразовать его, сохранить в атрибуте, передать это к другому методу, осмотрите это, напечатайте это & hellip; Тем не менее, к far самое обычное, что нужно сделать - это позвонить. Так, matz добавил еще один специализированный синтаксис ярлыка для этого общего случая: yield
означает «call
блок, который был передан методу». Поэтому нам не нужно имя:
def if; yield end
Итак, что является C # эквивалентом ключевого слова Ruby's 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); })