views:

143

answers:

5

Besides just using yield for iterators in Ruby, I also use it to pass control briefly back to the caller before resuming control in the called method. What I want to do in C# is similar. In a test class, I want to get a connection instance, create another variable instance that uses that connection, then pass the variable to the calling method so it can be fiddled with. I then want control to return to the called method so that the connection can be disposed. I guess I'm wanting a block/closure like in Ruby. Here's the general idea:

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
}

...

This doesn't work in C#; ReSharper tells me that the body of getThing cannot be an iterator block because MyThing is not an iterator interface type. That's definitely true, but I don't want to iterate through some list. I'm guessing I shouldn't use yield if I'm not working with iterators. Any idea how I can achieve this block/closure thing in C# so I don't have to wrap my code in MyTest1, MyTest2, ... with the code in getThing()'s body?

+12  A: 

What you want are lambda expressions, something like:

// 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();
});

This captures t the same way yield does in Ruby. The C# yield is different, it constructs generators that can be iterated over.

Blindy
That is crazy! Works perfectly, though. I may have seen `Action` before, but I never knew about its usage. Thanks!
Sarah Vessels
`Action` along with `Func` are the glue that hold LINQ together, they're designed to represent any lambda expression (up to 12 or so parameters I believe).
Blindy
A: 

yield in C# is specifically for returning bits of an iterated collection. Specifically, your function has to return IEnumerable<Thing> or IEnumerable for yield to work, and it's meant to be used from inside of a foreach loop. It is a very specific construct in c#, and it can't be used in the way you're trying.

I'm not sure off the top of my head if there's another construct that you could use or not, possibly something with lambda expressions.

Donnie
A: 

You can have GetThing take a delegate containing the code to execute, then pass anonymous methods from other functions.

SLaks
+1  A: 

I might pass a delegate into the iterator.

delegate void Action(MyThing myThing);
private static void forEachThing(Action action) 
{ 
    using (var connection = new Connection()) 
    { 
        action(new MyThing(connection));
    } 
}
ChrisW
Note that your definition of `Action` is flawed, it has to accept one parameter.
Blindy
@Blindy You're right, I'll correct it.
ChrisW
+1  A: 
Jörg W Mittag