开发者

How to use an IronRuby block with a C# method

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";


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.


For the most part, IronRuby Procs and lambdas are interchangeable with CLR Actions, Funcs, delegate types, and dynamic objects. However there's little Ruby syntactic sugar over this, other than some call-site conversions. Once place we did sweeten up the syntax was for .NET events; IronRuby allows passing a Ruby block as an CLR event handler: button.on_click {|s,e| ... }.

We have toyed a bunch of ways to allow blocks to be passed to CLR methods; either by detecting methods whose last argument is a callable object, or by allowing a special named parameter. There's a feature request (though cryptically named) already open for this: http://ironruby.codeplex.com/workitem/4511. Would be a good challenge for anyone willing to contribute.


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜