代码之家  ›  专栏  ›  技术社区  ›  Sarah Vessels

使用C中的yield,就像我在Ruby中使用的那样

  •  11
  • Sarah Vessels  · 技术社区  · 15 年前

    除了使用 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 不是迭代器接口类型。这是绝对正确的,但我不想重复一些列表。我想我不应该用 产量 如果我不使用迭代器。我知道如何在C语言中实现这个块/闭包,所以我不需要将代码包装在 MyTest1 , MyTest2 ,…代码在 getThing() 的身体?

    5 回复  |  直到 8 年前
        1
  •  19
  •   Blindy    15 年前

    您需要的是lambda表达式,类似于:

    // 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 在红宝石中。C语言 产量 不同的是,它构造可以迭代的生成器。

        2
  •  6
  •   Jörg W Mittag    8 年前

    你说你想用C yield 关键字的使用方法与使用Ruby的相同 产量 关键字。你似乎有点困惑这两个人到底在做什么:他们绝对有 没有什么 你们所要求的,是不可能的。

    C语言 产量 关键字是 C相当于红宝石 产量 关键字。事实上,那里 不是 相当于红宝石 产量 关键字在C中。红宝石相当于C 产量 关键字是 这个 产量 关键字 这是 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, 产量 用于生成 价值 呼叫者 在红宝石中, 产量 是用来屈服的 控制 到A 块论证

    事实上,在鲁比, 产量 只是一个捷径 Proc#call .

    想象一下,如果 产量 根本不存在。你怎么写 if Ruby中的方法?如下所示:

    class TrueClass
      def if(code)
        code.call
      end
    end
    
    class FalseClass
      def if(_); end
    end
    
    true.if(lambda { puts "It's true!" })
    

    这有点麻烦。在Ruby1.9中,我们得到了proc文本和 进程调用 使它变得更好:

    class TrueClass
      def if(code)
        code.()
      end
    end
    
    true.if(->{ puts "It's true!' })
    

    然而,松本由纪弘注意到, 巨大的 大多数高阶程序只需 过程参数。(尤其是因为Ruby在语言中内置了多个控制流构造,否则将需要多个过程参数,比如 if-then-else 需要两个和 case-when 因此,他创造了一种特殊的方法 恰好一 程序论证:程序块。(事实上,我们在一开始就看到了一个这样的例子,因为 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为这个常见的情况添加了另一种专门的快捷方式语法: 产量 意味着“ call 传递给方法的块”。因此,我们不需要名字:

    def if; yield end
    

    所以,什么 C相当于Ruby的 产量 关键字?好吧,让我们回到第一个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); })
    
        3
  •  1
  •   ChrisW    15 年前

    我可以将委托传递给迭代器。

    delegate void Action(MyThing myThing);
    private static void forEachThing(Action action) 
    { 
        using (var connection = new Connection()) 
        { 
            action(new MyThing(connection));
        } 
    }
    
        4
  •  0
  •   Donnie    15 年前

    yield 在C中,专门用于返回迭代集合的位。具体来说,您的函数必须返回 IEnumerable<Thing> IEnumerable 对于 产量 为了工作,它是从 foreach 循环。它是C中一个非常具体的结构,不能以您尝试的方式使用。

    我不确定是否还有其他构造可以使用,可能是lambda表达式的构造。

        5
  •  0
  •   SLaks    15 年前

    你可以拥有 GetThing 接受包含要执行的代码的委托,然后从其他函数传递匿名方法。