代码之家  ›  专栏  ›  技术社区  ›  Sinatr

拆分长插值字符串

  •  0
  • Sinatr  · 技术社区  · 9 年前

    例如:

    var a = $"Some value 1: {b1:0.00}\nSome value 2: {b2}\nSome value 3: {b3:0.00000}\nSome value 4: {b4:0.00}\nSome value 5: {b6:0.0}\nSome value 7: {b7:0.000000000}";
    

    这有点难以阅读来源。

    我能做到

    var a = $"Some value 1: {b1:0.00}\n" +
            $"Some value 2: {b2}\n" +
            $"Some value 3: {b3:0.00000}\n" +
            $"Some value 4: {b4:0.00}\n" +
            $"Some value 5: {b6:0.0}\n" +
            $"Some value 7: {b7:0.000000000}";
    

    但是 here 是否有评论指出这将是多次呼叫 string.Format 我想它会的(不知道如何检查它,IL对我来说还是个黑盒子)。

    问题:可以吗?拆分长插值字符串的其他选项有哪些?

    2 回复  |  直到 8 年前
        1
  •  7
  •   Community CDub    8 年前

    这将是对字符串的多次调用。格式,我想它会

    你说得对。你还没说你为什么在乎。为什么要避免这种情况?

    可以吗?

    我很好。

    拆分长插值字符串的其他选项有哪些?

    我会使用 逐字插入字符串 这会很好地解决你的问题。

    看见

    How do you use verbatim strings with interpolation?

    (因为这是你在问题中提到的链接,我不完全清楚你为什么要问这个问题,因为你已经阅读了一个建议好答案的页面。)

    我不喜欢$@这个主意,它比长字符串更糟糕

    你可能早就说过了。

    它不会被重新格式化的源意外损坏吗?

    可以通过更改源来更改所有代码。

    拆分长插值字符串的其他选项有哪些?

    首先不要插补。使字符串成为资源,使类负责获取格式化的资源字符串,并在类的方法中隐藏如何格式化字符串的实现细节。

        2
  •  3
  •   atlaste    9 年前

    编译器做什么?

    让我们从这里开始:

    var a = $"Some value 1: {b1:0.00}\n" +
            $"Some value 2: {b2}\n" +
            $"Some value 3: {b3:0.00000}\n" +
            $"Some value 4: {b4:0.00}\n" +
            $"Some value 5: {b6:0.0}\n" +
            $"Some value 7: {b7:0.000000000}";
    

    IL对我来说还是个黑盒子

    为什么不简单地打开它?使用ILSpy、Reflector等工具非常容易。

    代码中会发生的情况是,每一行都被编译为 string.Format 规则很简单:如果你有 $"...{X}...{Y}..." 它将编译为 string.Format("...{0}...{1}...", X, Y) 。还有 + 运算符将引入字符串连接。

    更详细地, string.Format格式 是一个简单的静态调用,这意味着编译器将使用 call 操作码而不是 callvirt .

    从所有这些可以推断出编译器很容易优化它:如果我们有一个表达式 constant string + constant string + ... 您可以简单地将其替换为 constant string 。您可以认为编译器了解 string.Format格式 以及字符串连接和处理。另一方面,你可以说它不应该。让我详细说明两个注意事项:

    请注意,字符串是.NET中的对象,但它们是“特殊对象”。你可以从以下事实中看出这一点: ldstr 操作码,但如果您查看如果 switch 在字符串上——编译器将生成字典。因此,由此可以推断编译器“知道” string 内部工作。让我们看看它是否知道如何连接,好吗?

    var str = "foo" + "bar";
    Console.WriteLine(str);
    

    在IL(当然是释放模式)中,这将给出:

    L_0000: ldstr "foobar"
    

    tl;博士: 因此,不管插值字符串的连接是否已经实现(它们还没有实现),我都很有信心编译器最终会处理这种情况。

    JIT做什么?

    下一个问题是:使用字符串的JIT编译器有多聪明?

    所以,让我们考虑一下,我们将教编译器关于 一串 首先,我们应该注意,C#被编译为IL,IL是JIT编译为汇编程序。如果是 转换 JIT编译器很难创建字典,因此我们必须在编译器中完成。另一方面,如果我们处理更复杂的连接,那么使用我们已经为f.ex.integer算术提供的东西来执行字符串操作也是有意义的。这意味着将字符串操作放入JIT编译器中。让我们用一个例子来考虑一下:

    var str = "";
    for (int i=0; i<10; ++i) {
        str += "foo";
    }
    Console.WriteLine(str);
    

    编译器将简单地将连接编译为IL,这意味着IL将保存这一点的非常直接的实现。在这种情况下,循环展开可以说对程序的(运行时)性能有很多好处:它可以简单地展开循环,将字符串附加10次,从而生成一个简单的常量。

    然而,将这些知识提供给JIT编译器会使其更加复杂,这意味着运行时将花费更多的时间进行JIT编译(找出优化),而执行(运行发出的汇编程序)的时间更少。剩下的问题是:会发生什么?

    启动程序,在写线上放置一个断点,然后点击ctrl-alt-D,查看汇编程序。

    00007FFCC8044413  jmp         00007FFCC804443F  
                {
                    str += "foo";
    00007FFCC8044415  mov         rdx,2BEE2093610h  
    00007FFCC804441F  mov         rdx,qword ptr [rdx]  
    00007FFCC8044422  mov         rcx,qword ptr [rbp-18h]  
    00007FFCC8044426  call        00007FFD26434CC0  
    
    [...]
    00007FFCC804443A  inc         eax  
    00007FFCC804443C  mov         dword ptr [rbp-0Ch],eax  
    00007FFCC804443F  mov         ecx,dword ptr [rbp-0Ch]  
    00007FFCC8044442  cmp         ecx,0Ah  
    00007FFCC8044445  jl          00007FFCC8044415  
    

    tl;博士: 不,这不是优化的。

    但我希望JIT也能优化它!

    是的,我不太确定我是否同意这个观点。运行时性能和JIT编译所花费的时间之间存在平衡。请注意,如果你在一个严密的循环中做这样的事情,我会认为你是在自找麻烦。另一方面,如果这是一个常见的、琐碎的情况(如连接的常量),那么很容易进行优化,并且不会影响运行时。

    换言之:可以说,您不希望JIT对其进行优化,假设这将花费太多时间。我相信我们可以相信微软会明智地做出这个决定。

    此外,您应该意识到.NET中的字符串是经过高度优化的。我们都知道他们经常被使用,微软也是如此。如果你不是在写“非常愚蠢的代码”,那么这是一个非常合理的假设,即它的性能会很好(除非得到证明)。

    选择?

    拆分长插值字符串的其他选项有哪些?

    使用资源。资源是处理多种语言的有用工具。如果这只是一个小型的、非专业的项目,我根本就不会费心。

    或者,您可以使用常量字符串串联的事实:

    var fmt = "Some value 1: {1:0.00}\n" +
              "Some value 2: {2}\n" +
              "Some value 3: {3:0.00000}\n" +
              "Some value 4: {4:0.00}\n" +
              "Some value 5: {6:0.0}\n" +
              "Some value 7: {7:0.000000000}";
    
    var a = string.Format(fmt, b1, b2, b3, b4, b5, b6, b7);