tags:

views:

132

answers:

2

I'm using IronRuby and trying to work out how to use a block with a C# method.

This is the basic Ruby code I'm attempting to emulate:

def BlockTest ()
  result = yield("hello")
  puts result
end

BlockTest { |x| x + " world" }

My attempt to do the same thing with C# and IronRuby is:

 string scriptText = "csharp.BlockTest { |arg| arg + 'world'}\n";
 ScriptEngine scriptEngine = Ruby.CreateEngine();
 ScriptScope scriptScope = scriptEngine.CreateScope();
 scriptScope.SetVariable("csharp", new BlockTestClass());
 scriptEngine.Execute(scriptText, scriptScope);

The BlockTestClass is:

public class BlockTestClass
{
    public void BlockTest(Func<string, string> block)
    {
        Console.WriteLine(block("hello "));
    }
}

When I run the C# code I get an exception of:

wrong number of arguments (0 for 1)

If I change the IronRuby script to the following it works.

 string scriptText = "csharp.BlockTest lambda { |arg| arg + 'world'}\n";

But how do I get it to work with the original IronRuby script so that it's the equivalent of my original Ruby example?

 string scriptText = "csharp.BlockTest { |arg| arg + 'world'}\n";
A: 

Ruby's blocks are not a concept understood by c# (or any of the other .Net languages).

To 'pass one' to the similar concept in c# of the delegate you must 'wrap it' in something that is understandable.

By making a lambda out of the block it becomes something you can pass to c# code expecting a delegate or expression.

This is a common issue with the 'Alt.Net' community, even for blessed languages like f# where 'functions pointers' are not implemented as delegates but instead are done slightly differently (FastFunc instances in f# for example) to pass one of these to something like your c# example would require wrapping it in a delegate (literally creating a delegate whose invocation passes the parameters to the underlying instance and returns the result back).

One could argue that such translation would be nicer if it was automatic, but doing that can lead to complex and strange edge cases or bugs and many developers prefer to know that such a wrapping operation will occurred just by looking at the code. It is also the case that there may not always be reasonable conversion (or more than one exists) so making the user decide what happens is a sensible default.

ShuggyCoUk
A: 

You can totally use ruby blocks in c#, in fact i have used this in an application that is currently in production! here is how:

In c# file:

public void BlockTest(dynamic block)
{
Console.WriteLine(block.call("world"));
}

In Ironruby:

#require the assembly
block = Proc.new {|i| "hello " + i }
Blah::Blah.BlockTest block

note: tested in c# 4.0 only

potlee
That's the same as using the lambda approach though. I was trying to find a way to use a block in the same intuitive way Ruby does without having to explicitly create a lambda or Proc object.
sipwiz