独自の関数を数式から呼べるように登録できます。組込関数と同じ仕組みの上に載るため、 依存再計算も参照シフトもそのまま働きます。
名前空間は unvell.ReoGrid.Core.Formula です。
登録する
using unvell.ReoGrid.Core;
using unvell.ReoGrid.Core.Formula;
// 引数が評価済みで渡される通常の関数
FunctionRegistry.Default.Eager("TAXINCL", (args, ctx) =>
{
if (args.Length < 1) return FormulaValue.Err(FormulaError.NA);
if (args[0].IsError) return args[0]; // エラーは伝播させる
double rate = args.Length > 1 ? args[1].Num : 0.1;
return FormulaValue.Number(args[0].Num * (1 + rate));
});
ws.SetFormula(0, 1, "TAXINCL(A1)");
ws.SetFormula(1, 1, "TAXINCL(A2, 0.08)");
関数名は大文字で登録してください。数式中の関数名は大文字に正規化されて照合されます。
登録は FunctionRegistry.Default に対して行うため、プロセス全体で共有されます。
アプリケーションの起動時に一度だけ行ってください。
FormulaValue
引数と戻り値の型です。
| 生成 | 種別 |
|---|---|
FormulaValue.Number(v) | 数値 |
FormulaValue.Text(s) | 文字列 |
FormulaValue.Logical(b) | 真偽値 |
FormulaValue.Date(serial) | 日付(OLE シリアル値) |
FormulaValue.Err(FormulaError.Value) | エラー |
| 判定・取り出し | 内容 |
|---|---|
IsError / IsNil / IsRange | 種別の判定 |
IsNumberLike | 数値・真偽値・日付のいずれか |
Num | 数値としての値 |
Str | 文字列としての値 |
var n = FormulaValue.Number(42);
var t = FormulaValue.Text("hello");
var b = FormulaValue.Logical(true);
var d = FormulaValue.Date(DateTime.Today.ToOADate());
var e = FormulaValue.Err(FormulaError.Value);
範囲を受け取る
範囲は展開されずに渡されます。中身が必要な場合は評価コンテキストから読みます。
FunctionRegistry.Default.Eager("COUNTPOSITIVE", (args, ctx) =>
{
int count = 0;
foreach (var a in args)
{
if (a.IsError) return a;
if (a.IsRange)
{
for (int r = a.R1; r <= a.R2; r++)
for (int c = a.C1; c <= a.C2; c++)
{
var v = ctx.GetCell(r, c, a.RangeSheet);
if (v.IsNumberLike && v.Num > 0) count++;
}
}
else if (a.IsNumberLike && a.Num > 0) count++;
}
return FormulaValue.Number(count);
});
a.RangeSheet は他シートの範囲を指している場合にそのシート名を持ちます(同一シートなら null)。
ctx.GetCell(row, col, sheet) に渡してください。
守るべき規約
- エラーは伝播させる。 引数がエラーなら、それをそのまま返すのが既定の作法です
- 引数不足は
#N/A、型不整合は#VALUE!を返す - 例外を投げない。 数式の評価中に例外が出ると再計算全体が失敗します。 エラー値を返してください
- 副作用を持たない。 再計算のタイミングと回数は保証されません
遅延評価
Lazy で登録すると、評価済みの値ではなく引数の構文木が渡されます。
ROW / COLUMN / OFFSET のように参照そのものを扱う関数に使います。
FunctionRegistry.Default.Lazy("MYREF", (argNodes, ctx) => { ... });
通常の計算関数には Eager を使ってください。
登録内容を確認する
bool exists = FunctionRegistry.Default.Has("TAXINCL");
int total = FunctionRegistry.Default.Count;
foreach (string name in FunctionRegistry.Default.Names)
Console.WriteLine(name);
保存との関係
カスタム関数を使った数式は、文字列としてそのまま保存されます。読み込み側で同じ関数が
登録されていなければ #NAME? になります。
XLSX として書き出した場合、Excel では当然その関数を解釈できません。 配布するファイルにカスタム関数を含めるかどうかは、用途に応じて判断してください。