我有两个Func<T,T2>
个对象将在一个将被重复调用的方法中使用(虽然这个方法只能从这个位置调用),声明{{1}会更有效吗?在父函数中(以便它们只需要实例化一次)并将它们传递给子函数或将它们放在子函数中(以便它们更接近使用)。
我不太了解Func
类或Func
编译器优化算法的内部工作原理。
出于说明目的:
情况1:
C#
情况2:
static void ThreadWorker(ref int current, ref int count)
{
bool isFinished = false;
while (!isFinished)
{
int workingValue = current++;
if (workingValue > TARGET)
{
isFinished = true;
}
else
{
if (EightyNineChain(workingValue))
{
count++;
}
}
}
}
private static bool EightyNineChain(int value)
{
Func<int, int[]> getDigits = v => v.ToString().Select(x => int.Parse(x.ToString()).ToArray();
Func<int[], int> getDigitSquareSum = x => (int)x.Select(d => Math.Pow(d, 2)).Sum();
//More code here
return result;
}
对我而言,情境1使代码更清晰,因为 static void ThreadWorker(ref int current, ref int count)
{
Func<int, int[]> getDigits = v => v.ToString().Select(x => int.Parse(x.ToString()).ToArray();
Func<int[], int> getDigitSquareSum = x => (int)x.Select(d => Math.Pow(d, 2)).Sum();
bool isFinished = false;
while (!isFinished)
{
int workingValue = current++;
if (workingValue > TARGET)
{
isFinished = true;
}
else
{
if (EightyNineChain(workingValue, getDigits, getDigitSquareSum))
{
count++;
}
}
}
}
private static bool EightyNineChain(int value, Func<int,int[]> getDigits, Func<int[],int> getDigitSquareSum)
{
//More code here
return result;
}
被宣布更接近他们使用的位置,因此它使其更具可读性。然而,逻辑告诉我情况2应该更快更有效。
答案 0 :(得分:1)
我认为这里最好的方法是使用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
}
}
查看IL test1()
。它必须新建几个对象并做其他一些事情。
现在查看test3()
的IL,它使用传入的Func<>
个参数。这样效率会更高,但请注意,它仍然需要callvirt
对Invoke()
Func<>
进行test2()
调用。
现在看看callvirt
,它使用本地函数。它只是直接调用函数而无需Invoke()
或test1()
。这显然好多了。
但请注意,在所有方法中,函数本身的代码在编译时只编译一次 - 即使对于test1()
,每次调用mutate_if
时也不会这样做,所以没有开销那个观点。