Volta 探検 (4): P/Invoke
いわゆる P/Invoke ですが,今回の“ネイティブコード”は JavaScript にあたります.以下のように Import 属性でコードごと埋め込むことができます.
public static class JSUtil { private const string setTimeoutExCode = @"function(handler, delayTime, arg) { var func = function() { handler(arg); }; setTimeout(func, delayTime); }"; [Import(setTimeoutExCode)] public static extern void SetTimeout(Action<int> handler, int delayTime, int arg); // こうすると実行時エラー // public static extern void SetTimeout<T>(Action<T> handler, int delayTime, T arg); }
これを使って先ほどのコードを書き直し.
using System; using Microsoft.LiveLabs.Volta.Html; using Microsoft.LiveLabs.Volta; namespace FizzBuzzVolta { public static class JSUtil { private const string setTimeoutExCode = @"function(handler, delayTime, arg) { var func = function() { handler(arg); }; setTimeout(func, delayTime); }"; [Import(setTimeoutExCode)] public static extern void SetTimeout(Action<int> handler, int delayTime, int arg); } public partial class VoltaPage1 : Page { public VoltaPage1() { InitializeComponent(); var button = new Button() { InnerText = "FizzBuzz!" }; var input = new Input() { Value = "1000" }; var para = new Paragraph() { button, new Span(){ InnerText = "upto: "}, input, }; Func<int, string> fizzbuzz = i => { if (i % 15 == 0) return "FizzBuzz"; else if (i % 5 == 0) return "Buzz"; else if (i % 3 == 0) return "Fizz"; else return i.ToString(); }; button.Click += () => { int upto = 0; if (!int.TryParse(input.Value, out upto)) return; this.Document.Body.Add(new Table() { new TableBody() { Id = "FizzBuzzTable" } }); Action<int> callback = null; callback = (int fizzBuzzCounter) => { if (fizzBuzzCounter < upto) { var tb = this.Document.GetById<TableBody>("FizzBuzzTable"); tb.Add(new TableRow() { new TableCell() { InnerText = fizzBuzzCounter.ToString() }, new TableCell() { InnerText = fizzbuzz(fizzBuzzCounter) } }); JSUtil.SetTimeout(callback, 0, fizzBuzzCounter + 1); } }; JSUtil.SetTimeout(callback, 0, 1); }; this.Document.Body.Add(para); } } }
一応,C# コードから JavaScript (Native) コードの呼び出しと,その逆両方がうまくいっているように見えます.
雰囲気としてはこんな感じでしょうか.
CLR on x86 | CLR on Volta on JavaScript | |
---|---|---|
Platform Invoke | .NET のメソッド呼び出しをアンマネージ関数呼び出し (関数ポインタへの jump) にマッピングできる | .NET のメソッド呼び出しを JavaScript コードにマッピングできる |
Reverse Platform Invoke (Callback) | マネージ関数を関数ポインタ (Thunk) として公開できる | マネージ関数を JavaScript の関数オブジェクトとして公開できる |