ここでの最良のアプローチは、C#7のローカル関数を使用することです。
は、次のコードを考えてみましょう:
using System;
using System.Linq;
namespace ConsoleApp1
{
public class Program
{
static void Main()
{
Console.WriteLine(test1(1));
Console.WriteLine(test2(1));
Func<int, int[]> getDigits = v => v.ToString().Select(Convert.ToInt32).ToArray();
Func<int[], int> getDigitSquareSum = x => (int)x.Select(d => Math.Pow(d, 2)).Sum();
Console.WriteLine(test3(1, getDigits, getDigitSquareSum));
}
static int test1(int value) // Use local Func<>
{
Func<int, int[]> getDigits = v => v.ToString().Select(Convert.ToInt32).ToArray();
Func<int[], int> getDigitSquareSum = x => (int)x.Select(d => Math.Pow(d, 2)).Sum();
var a = getDigits(value);
var b = getDigitSquareSum(a);
return b;
}
static int test2(int value) // Use local function.
{
int[] digits(int v) => v.ToString().Select(Convert.ToInt32).ToArray();
int digitSquareSum(int[] x) => (int) x.Select(d => Math.Pow(d, 2)).Sum();
var a = digits(value);
var b = digitSquareSum(a);
return b;
}
// Pass in Func<>
static int test3(int value, Func<int, int[]> getDigits, Func<int[], int> getDigitSquareSum)
{
var a = getDigits(value);
var b = getDigitSquareSum(a);
return b;
}
}
}
これは、次のILコードに変換されます。
.class public auto ansi beforefieldinit ConsoleApp1.Program
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
}
.method private hidebysig static void Main() cil managed
{
.entrypoint
.locals init (
[0] class [mscorlib]System.Func`2<int32, int32[]> getDigits,
[1] class [mscorlib]System.Func`2<int32[], int32> getDigitSquareSum
)
IL_0000: ldc.i4.1
IL_0001: call int32 ConsoleApp1.Program::test1(int32)
IL_0006: call void [mscorlib]System.Console::WriteLine(int32)
IL_000b: ldc.i4.1
IL_000c: call int32 ConsoleApp1.Program::test2(int32)
IL_0011: call void [mscorlib]System.Console::WriteLine(int32)
IL_0016: ldsfld class [mscorlib]System.Func`2<int32, int32[]> ConsoleApp1.Program/'<>c'::'<>9__0_0'
IL_001b: dup
IL_001c: brtrue.s IL_0035
IL_001e: pop
IL_001f: ldsfld class ConsoleApp1.Program/'<>c' ConsoleApp1.Program/'<>c'::'<>9'
IL_0024: ldftn instance int32[] ConsoleApp1.Program/'<>c'::'<Main>b__0_0'(int32)
IL_002a: newobj instance void class [mscorlib]System.Func`2<int32, int32[]>::.ctor(object, native int)
IL_002f: dup
IL_0030: stsfld class [mscorlib]System.Func`2<int32, int32[]> ConsoleApp1.Program/'<>c'::'<>9__0_0'
IL_0035: stloc.0
IL_0036: ldsfld class [mscorlib]System.Func`2<int32[], int32> ConsoleApp1.Program/'<>c'::'<>9__0_1'
IL_003b: dup
IL_003c: brtrue.s IL_0055
IL_003e: pop
IL_003f: ldsfld class ConsoleApp1.Program/'<>c' ConsoleApp1.Program/'<>c'::'<>9'
IL_0044: ldftn instance int32 ConsoleApp1.Program/'<>c'::'<Main>b__0_1'(int32[])
IL_004a: newobj instance void class [mscorlib]System.Func`2<int32[], int32>::.ctor(object, native int)
IL_004f: dup
IL_0050: stsfld class [mscorlib]System.Func`2<int32[], int32> ConsoleApp1.Program/'<>c'::'<>9__0_1'
IL_0055: stloc.1
IL_0056: ldc.i4.1
IL_0057: ldloc.0
IL_0058: ldloc.1
IL_0059: call int32 ConsoleApp1.Program::test3(int32, class [mscorlib]System.Func`2<int32, int32[]>, class [mscorlib]System.Func`2<int32[], int32>)
IL_005e: call void [mscorlib]System.Console::WriteLine(int32)
IL_0063: ret
}
.method private hidebysig static int32 test1 (
int32 'value'
) cil managed
{
.locals init (
[0] class [mscorlib]System.Func`2<int32, int32[]> getDigits,
[1] int32[] a
)
IL_0000: ldsfld class [mscorlib]System.Func`2<int32, int32[]> ConsoleApp1.Program/'<>c'::'<>9__1_0'
IL_0005: dup
IL_0006: brtrue.s IL_001f
IL_0008: pop
IL_0009: ldsfld class ConsoleApp1.Program/'<>c' ConsoleApp1.Program/'<>c'::'<>9'
IL_000e: ldftn instance int32[] ConsoleApp1.Program/'<>c'::'<test1>b__1_0'(int32)
IL_0014: newobj instance void class [mscorlib]System.Func`2<int32, int32[]>::.ctor(object, native int)
IL_0019: dup
IL_001a: stsfld class [mscorlib]System.Func`2<int32, int32[]> ConsoleApp1.Program/'<>c'::'<>9__1_0'
IL_001f: stloc.0
IL_0020: ldsfld class [mscorlib]System.Func`2<int32[], int32> ConsoleApp1.Program/'<>c'::'<>9__1_1'
IL_0025: dup
IL_0026: brtrue.s IL_003f
IL_0028: pop
IL_0029: ldsfld class ConsoleApp1.Program/'<>c' ConsoleApp1.Program/'<>c'::'<>9'
IL_002e: ldftn instance int32 ConsoleApp1.Program/'<>c'::'<test1>b__1_1'(int32[])
IL_0034: newobj instance void class [mscorlib]System.Func`2<int32[], int32>::.ctor(object, native int)
IL_0039: dup
IL_003a: stsfld class [mscorlib]System.Func`2<int32[], int32> ConsoleApp1.Program/'<>c'::'<>9__1_1'
IL_003f: ldloc.0
IL_0040: ldarg.0
IL_0041: callvirt instance int32[] class [mscorlib]System.Func`2<int32, int32[]>::Invoke(!0)
IL_0046: stloc.1
IL_0047: ldloc.1
IL_0048: callvirt instance int32 class [mscorlib]System.Func`2<int32[], int32>::Invoke(!0)
IL_004d: ret
}
.method private hidebysig static int32 test2 (
int32 'value'
) cil managed
{
IL_0000: ldarg.0
IL_0001: call int32[] ConsoleApp1.Program::'<test2>g__digits2_0'(int32)
IL_0006: call int32 ConsoleApp1.Program::'<test2>g__digitSquareSum2_1'(int32[])
IL_000b: ret
}
.method private hidebysig static int32 test3 (
int32 'value',
class [mscorlib]System.Func`2<int32, int32[]> getDigits,
class [mscorlib]System.Func`2<int32[], int32> getDigitSquareSum
) cil managed
{
.locals init (
[0] int32[] a
)
IL_0000: ldarg.1
IL_0001: ldarg.0
IL_0002: callvirt instance int32[] class [mscorlib]System.Func`2<int32, int32[]>::Invoke(!0)
IL_0007: stloc.0
IL_0008: ldarg.2
IL_0009: ldloc.0
IL_000a: callvirt instance int32 class [mscorlib]System.Func`2<int32[], int32>::Invoke(!0)
IL_000f: ret
}
}
はtest1()
のためのILを見てみましょう。いくつかのオブジェクトを新しくしなければならず、他にもたくさんのことをする必要があります。
test3()
のILを調べ、渡されたパラメータはFunc<>
です。これははるかに効率的ですが、それでもへのcallvirt
コールをFunc<>
で行う必要があることに注意してください。
ローカル機能を使用するtest2()
を参照してください。それはcallvirt
またはInvoke()
を必要とせずに直接関数を呼び出すだけです。それは明らかにずっと良いです。
しかし、すべてのメソッドでは、関数自体のコードはコンパイル時に一度だけコンパイルされることに注意してください。test1()
でも、test1()
が呼び出されるたびに実行されないため、その観点からオーバーヘッドはありません。
これらが動的な 'Func <>'の理由はありますか?なぜそれらを通常の機能にしないのですか? – nvoigt
正直なところ、この特定の文脈では、それらは規則的な機能である可能性があり、今私はそれらを変更しました(ちょうど怠け者でした)が、私の質問はまだ有用な回答をもたらす可能性があると信じています –