君の瞳はまるでルビー - Ruby 関連まとめサイト

IronRuby を動かす際の注意点

最終更新: 2015-03-31 (火) 21:53:18 (2920d)

IronRuby を使うと .NET Framework ⇔ Ruby 間で任意のやりとりが簡単にできます。

たいていの場合、IronRuby が2つの間を取り持ってくれますが、注意しないと意図した通りに動かない場合があります。

僕がつまづいた注意点をまとめていきます。

.NET Framework から IronRuby

ハッシュを渡すときの注意!

ハッシュを渡したはずなのに IronRuby 側でうまく値の取得ができない…。puts でハッシュの中身を表示させるとちゃんと入っているように見えるのに…なんで!?

文字列をキーとしたハッシュを事例として挙げます。ポイントは以下の2つです。

  • IronRuby.Builtins.Hash を使うこと。
  • キーに IronRuby.Builtins.MutableString.Create を使うこと。

事例を示します。

以下のような Ruby コードがあったとします。

# example.rb
def function1(hash)
  puts hash["key1"]
end

上記の Ruby コードを読み込み、.NET Framework から function1 メソッドを呼び出します。

ScriptEngine engine = IronRuby.Ruby.CreateRuntime().GetEngine("Ruby");
ScriptScope scope = engine.CreateScope();
engine.ExecuteFile("example.rb", scope);

IronRuby.Builtins.Hash hash = new IronRuby.Builtins.Hash(new Dictionary<object, object>());
hash[IronRuby.Builtins.MutableString.Create("key1")] = "value1";
hash[IronRuby.Builtins.MutableString.Create("key2")] = "value2";
hash[IronRuby.Builtins.MutableString.Create("key3")] = "value3";

engine.Operations.InvokeMember(scope, "function1", hash);

Object::TOPLEVEL_BINDING を使う場合の注意!

MemberAccessException(uninitialized constant Object::TOPLEVEL_BINDING)に見舞われます。

ir コマンドでは動くコードなのに、なぜか .NET Framework から呼び出すと失敗します。

以下のコード断片を Ruby コードに追加することで回避できます。

class Object
 TOPLEVEL_BINDING = binding unless defined?(TOPLEVEL_BINDING)
end

書き換え後のソースは CRuby/IronRuby 双方で問題なく動作します。

(未解決)require した先に定義してあるメソッドを呼び出すときの注意!

以下のような2つの Ruby コードがあったとします。

# example1.rb
def function1
  puts "function1"
end
# example2.rb
require "example1"

example2.rb を読み込んで function1 を呼びだそうとすると…

ScriptEngine engine = IronRuby.Ruby.CreateRuntime().GetEngine("Ruby");
ScriptScope scope = engine.CreateScope();
engine.ExecuteFile("example2.rb", scope);

engine.Operations.InvokeMember(scope, "function1");

System.MissinMethodException に見舞われます…。

例外のメッセージは以下の通りです。

private method `function1' called

なぜでしょうか?理由はよくわかりません。

ちなみに engine.Operations.InvokeMember ではなく engine.Execute で処理を呼び出すことはできます。ただし、引数が渡せません!

今のところわかっている回避策は、example1.rb を以下のように書き換え public にする方法です。

# example1.rb
public
def function1
  puts "function1"
end

すると問題なく engine.Operations.InvokeMember で function1 を呼び出せるようになります。

この修正は文法上問題はないようなので、書き換え後のソースは CRuby/IronRuby 双方で問題なく動作します。

(未解決)モジュールメソッドの呼び出し方がわからない!!

以下のような Ruby コードがあったとして Example::method1 を .NET Framework から呼び出す方法がわからない!

# example.rb
module Example
  def self.method1
    puts "method1"
  end
end

以下のコードでは呼び出せません!!

ScriptEngine engine = IronRuby.Ruby.CreateRuntime().GetEngine("Ruby");
ScriptScope scope = engine.CreateScope();
engine.ExecuteFile("example.rb", scope);

engine.Operations.InvokeMember(scope, "Example::method1")

IronRuby から .NET Framework

論理値の戻り値に注意!

Ruby で論理式を実行した結果を戻り値として受け取ったので bool 型にキャストしようとしたら InvalidCastException や NullReferenceException が発生した…なんで?

例えば正規表現のマッチング結果は true/false ではありません。

以下の事例のように正規表現のマッチング結果を return する IronRuby のプログラムがあった場合、 .NET Framework 側では単純な bool へのキャストでは対応できません。

# example.rb
def function1(text)
  return text =~ /abc/
end

Ruby では false または nil だけが偽で、それ以外は 0 や空文字列も含め全て真です。

以下のような論理値変換用のメソッドを用意しておくのが良いでしょう。

public static bool ToBool(object obj)
{
    if (obj != null)
    {
        return (obj is bool) ? (bool)obj : true;
    }
    else
    {
        return false;
    }
}

上記のメソッドを使って論理値を得ます。

ScriptEngine engine = IronRuby.Ruby.CreateRuntime().GetEngine("Ruby");
ScriptScope scope = engine.CreateScope();
engine.ExecuteFile("example.rb", scope);

bool falseResult = ToBool(engine.Operations.InvokeMember(scope, "function1", "a"));
bool tureResult  = ToBool(engine.Operations.InvokeMember(scope, "function1", "abc"));

コメント

本ページの内容に関して何かコメントがある方は、以下に記入してください。

コメントはありません。 コメント/ironruby/note

お名前: