Golang中C#语法(字符串插值?)的讨论
Golang中C#语法(字符串插值?)的讨论 在 C# 中,我特别喜欢的一个功能是能够这样写:
int age = 80;
string s = $"I am {age} years old.";
Go 语言是否有可以实现类似功能的包?fmt 包是否支持类似的语法?我当然知道下面的语法,但在我看来,它有点冗长,并且不那么“美观”。尤其是在有多个参数时:
age:=80
s:=fmt.Sprintf("I am %d years old.", age)
skillian:
你可以使用
go/*包来编写一个 go:generate 程序
这对我来说是新的(我是 Go 新手),你有我可以学习的链接吗,或者我应该去谷歌搜索一下 😊
更多关于Golang中C#语法(字符串插值?)的讨论的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我不确定在 Go 中如何通过库来实现这一点;你需要编译器的支持。
是的,仔细想想,这听起来是对的。看来我只能等待,看看这个功能将来是否会实现。至少他们已经在考虑这个问题了(参见我上面的链接)。
我刚刚发现了这个链接:
议题:字符串插值
简介 为那些不知道这是什么的人解释一下: Swift
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message...
标签: Go2, LanguageChange, Proposal
显然,这已经被提议作为语言扩展,我想它将来可能会出现。
C# 编译器通常将 $"Hello, {whom}!"; 翻译成 String.Format("Hello {0}!", whom); 或 String.Concat("Hello, ", whom, "!"); (链接)。我不确定在 Go 中如何通过库来实现这一点;这需要编译器的支持。你可以使用 go/* 包来编写一个 go:generate 程序,将插值字符串转换为对 fmt.Sprintf 或 strings.Join 的类似调用。
func main() {
fmt.Println("hello world")
}
最接近的方法可能是以下几种,不知道是否有第五种(或更多)方法,这里考虑三种加上你的方法,总共是四种:
package main
import (
"fmt"
"strconv"
"strings"
)
func main() {
age := 80
s1 := fmt.Sprint("I am ", age, " years old.")
// 或者使用 Sprintln 在字段前后自动插入空格
s := fmt.Sprintln("I am", age, "years old.")
s2 := "I am " + strconv.Itoa(age) + " years old."
s3 := strings.Join([]string{"I am", strconv.Itoa(age), "years old."}, " ")
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
}
Hultan:
string s = $“I am {age:0.##} years old.”;
我能找到的最接近的匹配,虽然可能不够优雅,是:
package main
import (
"fmt"
)
func main() {
age := 80.5345
s := fmt.Sprintln("I am", fmt.Sprintf("%0.2f", age), "years old.")
fmt.Println(s)
}
或者,你可能喜欢自己写一个函数,例如:
package main
import (
"fmt"
)
func main() {
age := 80.5345
s := fmt.Sprintln("I am", interpolate("%0.2f", age), "years old.")
fmt.Println(s)
}
func interpolate(f string, s float64) string {
return fmt.Sprintf(f, s)
}
感谢你的建议。
不过说实话,我认为你的建议中可能比我提出的方案看起来更好的,也许只有第一个建议(s1)。它至少把参数放在了正确的位置。但我仍然认为这个解决方案不如C#的方案简洁明了。
而且,我在最初的问题中没有提到这一点,但在C#中,你还可以像这样提供格式化设置:
float age = 80.5345;
string s = $"I am {age:0.##} years old.";
这将输出“I am 80.53 years old.”。这使得它功能更加强大。据我所知,在Go中实现这一点的唯一方法仍然是我最初提出的“解决方案”,但那样我们又回到了起点,变量 age 又不在正确的位置了。
age:=80.5345
s:=fmt.Sprintf("I am %.2f years old.", age)
不过,如果没有人开发出类似C#语法的包,我会感到惊讶。
我强烈建议查看 stringer 的源代码,它会为你指定的类型生成 String 函数。
虽然有点自卖自夸,但我认为这很相关:我改编了 stringer 的源代码,以生成一个类似 plugin 的 API,用于通过名称访问常量、全局变量、函数和类型。这样你就可以做到,例如:
package main
import "github.com/skillian/pkgsyms"
//go:generate pkgsyms -package "fmt"
var fmtPkg = pkgsyms.Of("fmt")
func main() {
fmtPkg.Lookup("Println").Value.(func(args ...interface{}) (int, error))("hello, world!")
}
我是用手机写的,所以我不确定上面的代码是否能编译,当然这也不是重点。这更多是为了让我能在配置文件中写入类型名称,以便为我正在进行的其他项目实现依赖注入,不过有点跑题了!
我的观点是,你可以查看 stringer 的源代码,并将其与 pkgsyms 进行比较,看看为了处理读取 Go 源代码文件,你需要做哪些改动。stringer 和 pkgsyms 都进行了一些字符串格式化来生成单独的输出文件。它们并不转换实际的输入源代码,而如果你想实现字符串插值,可能就需要这样做。为此,你必须使用 astutil 包。遗憾的是,我现在无法提供任何示例,因为我从未使用过那个包。
在 Go 语言中,虽然没有像 C# 那样直接的字符串插值语法,但可以通过几种方式实现类似效果。最接近的方式是使用 fmt.Sprintf,这是标准库提供的格式化字符串方法。
示例代码:
package main
import (
"fmt"
)
func main() {
age := 80
// 使用 fmt.Sprintf 实现类似功能
s := fmt.Sprintf("I am %d years old.", age)
fmt.Println(s) // 输出: I am 80 years old.
// 多参数示例
name := "John"
score := 95.5
message := fmt.Sprintf("%s is %d years old and scored %.1f on the test.", name, age, score)
fmt.Println(message) // 输出: John is 80 years old and scored 95.5 on the test.
}
其他替代方案:
- 使用
strings.Builder进行高效拼接(适用于复杂场景):
package main
import (
"strconv"
"strings"
)
func main() {
age := 80
var sb strings.Builder
sb.WriteString("I am ")
sb.WriteString(strconv.Itoa(age))
sb.WriteString(" years old.")
s := sb.String()
println(s) // 输出: I am 80 years old.
}
- 使用模板引擎(适用于复杂模板):
package main
import (
"bytes"
"text/template"
)
func main() {
age := 80
tmpl, _ := template.New("test").Parse("I am {{.}} years old.")
var buf bytes.Buffer
tmpl.Execute(&buf, age)
s := buf.String()
println(s) // 输出: I am 80 years old.
}
性能说明:
fmt.Sprintf在可读性和性能之间取得了平衡,是 Go 社区最常用的方式strings.Builder在需要大量字符串拼接时性能更优- 模板引擎适用于需要复杂逻辑或外部模板文件的场景
Go 团队在语言设计上倾向于显式而非隐式,因此虽然语法上不如 C# 简洁,但 fmt.Sprintf 的方式在类型安全和可读性上具有优势。目前 Go 语言标准库中没有提供直接的字符串插值语法,上述方法是实际开发中的标准做法。

