【问题标题】:How to make inline functions in C#如何在 C# 中创建内联函数
【发布时间】:2011-02-04 16:03:08
【问题描述】:

我正在使用 Linq To XML

new XElement("Prefix", Prefix == null ? "" : Prefix)

但我想在将前缀添加到 xml 之前对前缀进行一些计算,例如消除空格、特殊字符、一些计算等

我不想创建函数,因为除了这个之外,这些函数对我程序的任何其他部分都没有任何帮助,那么有什么方法可以创建内联函数吗??

【问题讨论】:

  • 不要因为不会从其他地方调用它就禁止自己创建函数。函数也可用于清理代码(如果你有一个跨越几页的大过程,将其分解成函数使其更具可读性)和自我文档(只需正确命名函数即可向读者传达信息。)
  • 如果您是从 Google 来到这里并想知道如何在 c# 中模拟内联函数,请转到此处:stackoverflow.com/questions/473782/inline-functions-in-c 以下答案与真正意义上的“内联”函数无关这个词,c#甚至没有真正的内联函数,但上面的链接提供了一个可以使用的编译器优化。
  • 谢谢詹姆斯,这个问题的标题确实有点误导。
  • 如果您是从 Google 来到这里,并且您不想挖掘 - 只需输入此属性:[MethodImpl(MethodImplOptions.AggressiveInlining)]

标签: c# .net


【解决方案1】:

是的,C# 支持这一点。有几种可用的语法。

  • Anonymous methods 在 C# 2.0 中添加:

    Func<int, int, int> add = delegate(int x, int y)
    {
        return x + y;
    };
    Action<int> print = delegate(int x)
    {
        Console.WriteLine(x);
    }
    Action<int> helloWorld = delegate // parameters can be elided if ignored
    {
        Console.WriteLine("Hello world!");
    }
    
  • Lambdas 是 C# 3.0 中的新功能,有两种风格。

    • 表达式 lambda:

      Func<int, int, int> add = (int x, int y) => x + y; // or...
      Func<int, int, int> add = (x, y) => x + y; // types are inferred by the compiler
      
    • 语句 lambda:

      Action<int> print = (int x) => { Console.WriteLine(x); };
      Action<int> print = x => { Console.WriteLine(x); }; // inferred types
      Func<int, int, int> add = (x, y) => { return x + y; };
      
  • Local functions 已在 C# 7.0 中引入:

    int add(int x, int y) => x + y;
    void print(int x) { Console.WriteLine(x); }
    

它们基本上有两种不同的类型:FuncActionFuncs 返回值,但 Actions 不返回。 Func 的最后一个类型参数是返回类型;其他都是参数类型。

类似的类型有不同的名称,但声明它们内联的语法是相同的。这方面的一个例子是Comparison&lt;T&gt;,它大致相当于Func&lt;T, T, int&gt;

Func<string, string, int> compare1 = (l,r) => 1;
Comparison<string> compare2 = (l, r) => 1;
Comparison<string> compare3 = compare1; // this one only works from C# 4.0 onwards

这些可以像常规方法一样直接调用:

int x = add(23, 17); // x == 40
print(x); // outputs 40
helloWorld(x); // helloWorld has one int parameter declared: Action<int>
               // even though it does not make any use of it.

【讨论】:

  • 我不明白这个答案。在他的示例中,他希望 new XElement("Prefix", prefix => newPrefix) 内联声明。所有这些答案都相当于声明了一个新功能。为什么不直接声明一个新函数?
  • 因为:1. 理解读取内联函数的方法逻辑比查找不同的声明要容易得多 2. 使用内联函数时经常需要编写更少的代码
  • 我正在使用 Unity3D,对于所有密集的开箱即用目​​的来说,它是 C#
【解决方案2】:

C# 7 增加了对 local functions 的支持

这是前面使用局部函数的示例

void Method()
{
    string localFunction(string source)
    {
        // add your functionality here
        return source ;
    };

   // call the inline function
   localFunction("prefix");
}

【讨论】:

  • 该函数会被创建多少次?是只创建还是每次调用包装函数时创建?
  • 内联函数可以是 lambda 函数吗?
  • Yes from this example do string localFunction(string source) =&gt; source; Read the sections on Definite assignment and Variable capture,这应该可以回答您的问题。
【解决方案3】:

您的问题的答案是肯定的和否定的,这取决于您所说的“内联函数”是什么意思。如果您使用的术语与 C++ 开发中使用的术语类似,那么答案是否定的,您不能这样做——即使 lambda 表达式也是函数调用。虽然您确实可以定义内联 lambda 表达式来替换 C# 中的函数声明,但编译器最终仍会创建一个匿名函数。

这是我用来测试这个的一些非常简单的代码(VS2015):

    static void Main(string[] args)
    {
        Func<int, int> incr = a => a + 1;
        Console.WriteLine($"P1 = {incr(5)}");
    }

编译器生成什么?我使用了一个名为 ILSpy 的漂亮工具,它显示了生成的实际 IL 程序集。看看(我省略了很多类设置的东西)

这是主函数:

        IL_001f: stloc.0
        IL_0020: ldstr "P1 = {0}"
        IL_0025: ldloc.0
        IL_0026: ldc.i4.5
        IL_0027: callvirt instance !1 class [mscorlib]System.Func`2<int32, int32>::Invoke(!0)
        IL_002c: box [mscorlib]System.Int32
        IL_0031: call string [mscorlib]System.String::Format(string, object)
        IL_0036: call void [mscorlib]System.Console::WriteLine(string)
        IL_003b: ret

看到那些行 IL_0026 和 IL_0027 了吗?这两条指令加载数字 5 并调用一个函数。然后将 IL_0031 和 IL_0036 格式化并打印结果。

下面是调用的函数:

        .method assembly hidebysig 
            instance int32 '<Main>b__0_0' (
                int32 a
            ) cil managed 
        {
            // Method begins at RVA 0x20ac
            // Code size 4 (0x4)
            .maxstack 8

            IL_0000: ldarg.1
            IL_0001: ldc.i4.1
            IL_0002: add
            IL_0003: ret
        } // end of method '<>c'::'<Main>b__0_0'

这是一个非常短的函数,但它是一个函数。

这值得努力优化吗?不。也许如果您每秒调用它数千次,但如果性能如此重要,那么您应该考虑调用用 C/C++ 编写的本机代码来完成这项工作。

根据我的经验,可读性和可维护性几乎总是比优化几微秒的速度更重要。使用函数使您的代码具有可读性并控制变量范围,而不必担心性能。

“过早的优化是编程中万恶(或至少大部分)的根源。” ——唐纳德·克努斯

“不能正确运行的程序不需要运行得很快” -- 我

【讨论】:

  • 这是内联 C# 函数的热门 Google 热门歌曲。你说“如果你每秒调用它数千次”,你猜怎么着 - 我每秒调用它 400 万次,所以是的......内联可能很有用(并且大大提高代码的可读性与什么相比)我即将要做的事:在 16 个地方手动展开一项功能)。
  • 对于熟悉 Kotlin 的人来说,这意味着 Kotlin 的 inline 函数在 C# 中没有等效的。
【解决方案4】:

是的。

您可以创建anonymous methodslambda expressions

Func<string, string> PrefixTrimmer = delegate(string x) {
    return x ?? "";
};
Func<string, string> PrefixTrimmer = x => x ?? "";

【讨论】:

【解决方案5】:

您可以使用Func,它封装了一个方法,该方法有一个参数并返回一个由TResult参数指定的类型的值。

void Method()
{
    Func<string,string> inlineFunction = source => 
    {
        // add your functionality here
        return source ;
     };


    // call the inline function
   inlineFunction("prefix");
}

【讨论】:

    【解决方案6】:

    不仅在方法内部,也可以在类内部使用。

    class Calculator
        {
            public static int Sum(int x,int y) => x + y;
            public static Func<int, int, int>  Add = (x, y) => x + y;
            public static Action<int,int> DisplaySum = (x, y) => Console.WriteLine(x + y);
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-28
      • 2014-06-15
      • 2020-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多