一只萌萌小番薯
您不能在函数调用的返回值中修改类型的方法集myStruct,因此在您编写的“装饰器”函数中直接扩展结构的行为是不可能的。我实际上是在使用这个装饰器来包装fmt.Sprintf, Sprintln,Sprint方法来获取Logger.Debugln, Logger.Debugf, Logger.Warning, ... 方法,其中Logger是自定义结构,因为这些函数共享几乎相同的行为。您也无法以通用方式传递fmt.Sprintf,fmt.Sprintln和函数,因为它们共享不同的签名:fmt.Sprintfunc Print(a ...interface{}) (n int, err error)
func Printf(format string, a ...interface{}) (n int, err error)
func Println(a ...interface{}) (n int, err error)所以你需要单独包装这些方法。我并不完全清楚您如何寻求包装这些方法来生成您的Logger类型。如果您需要在记录器的顶层使用这些方法中的每一个,则需要在您的日志包或Logger类型的接口中显式声明它们。尽管它们有明显的相似之处,但您不能在运行时动态绑定每个级别的功能;go 是一种静态类型语言,因此必须预先声明您的类型。显式定义类型将是最清晰的解决方案,这也符合一句关键的谚语:“清晰胜于聪明”。如有必要,您可以将每个方法声明为您类型上实际执行打印逻辑的内部方法的简单包装,从而将重复保持在最低限度。Debug这是我拼凑在一起的一个简单示例,用于显示某些,Debugf和端点的逻辑Debugln。您可以看到核心逻辑由类型上的一组通用方法处理Logger,您可以想象如何轻松实现其他日志级别的端点。如果重复确实是一个问题,您可以简单地编写一个代码生成器来自动生成日志级别的特定方法,调用在各种打印函数中明确声明的核心功能。请记住,您可以在同一包中的多个源文件中为一个类型声明接收器,从而允许在某些源文件中生成某些方法,而其他方法则在其他地方显式实现。package mainimport ( "fmt" "io" "os")type Level stringconst ( Debug Level = "DEBUG" Error = "ERROR"// etc.)type Logger struct { writer io.Writer prependLevel bool}func (l *Logger) print(level Level, msg string) { var s string if l.prependLevel { s = string(level) + ": " } s += msg fmt.Fprint(l.writer, s)}func (l *Logger) printf(level Level, format string, a ...interface{}) { l.print(level, fmt.Sprintf(format, a...))}func (l *Logger) println(level Level, msg string) { l.printf(level, "%s\n", msg)}func (l *Logger) Debug(msg string) { l.print(Debug, msg)}func (l *Logger) Debugf(format string, a ...interface{}) { l.printf(Debug, format, a...)}func (l *Logger) Debugln(msg string) { l.println(Debug, msg)}func main() { logger := Logger{os.Stderr, true} logger.Debugln("A plain message with a new line") logger.Debugf( ("The most customizable log entry with some parameters: " + "%s %v\n"), "myStr", false, ) logger.Debug("A bare print statement")}第三方包如果您还没有这样做,我建议您考虑使用 Go 中的许多第三方日志记录包,或者至少评估它们的接口以确定其他人如何处理类似任务。例如,Logrus 是一个带有级别的 Go 记录器。您可以在他们的源文件中看到他们如何在每个日志级别为每个函数显式声明接口,同时使用上述方法的更全面版本来减少核心日志实现中的重复:Interface Formatter implementations