在其他包中定义的类型的方法集能被修改吗?

huangapple go评论83阅读模式
英文:

Can the method set of a type defined in other package be modified?

问题

内置的loglog.Logger没有像Python那样提供ErrorWarning方法。

所以我想为内置的Logger类型编写一个如下的代码:

func (l *Logger) Error(v interface{}) {
    info := fmt.Sprintf("ERROR: %v", v)
    l.Println(info)
}

我将上述代码放在GOPATH/src/log目录下的morelog.go文件中。

main.go中,我写了以下代码:

logger := log.New(os.Stdout, "Test", 1)
logger.Error("Error in main.")

当我运行go build时,我得到了以下错误:

./main.go:124: logger.Error undefined (type *log.Logger has no field or method Error)

我知道我可以通过定义一个新类型并在该类型上定义方法来实现类似的目标。但是我认为如果我可以直接将方法添加到内置类型中会更好。

英文:

The built-in log and log.Logger don't provide Error or Warning methods like Python.

So I would like to write one for the built-in Logger type with the following code:

func (l *Logger) Error(v interface{}) {
    info := fmt.Sprintf("ERROR: %v", v)
    l.Println(info)
}

And I put the code above in a file morelog.go under GOPATH/src/log.

And in the main.go I wrote:

logger := log.New(os.Stdout, "Test", 1)
logger.Error("Error in main.")

When I run go build, I get:

./main.go:124: logger.Error undefined (type *log.Logger has no field or method Error)

I know I can achieve the similar goal by defining a new type and define methods on that type. But I think it could be better if I can ad the method directly to the buit-in type.

答案1

得分: 5

这两个答案都是正确的,所以我再提供几个替代方案。你不能在你没有定义的类型上定义方法。函数确实是一种方法,你可以通过重新定义类型或包装类型来实现。

例如,如果你不关心拆分输出文件,只想修改日志行,那么你可以选择以下其中一种方法。

重新定义类型:

type MyLogger log.Logger

func (l MyLogger) Info(msg string, args ...interface{}) {
    log.Logger(l).Printf(msg, args...)
}

func (l MyLogger) Error(msg string, args ...interface{} {
    log.Logger(l).Printf("ERROR: " + msg, args...)
}

或者包装类型:

type MyLogger struct {
  log.Logger
}

func (l MyLogger) Info(msg string, args ...interface{}) {
  l.Printf(msg, args...)
}

func (l MyLogger) Error(msg string, args ...interface{}) {
  l.Printf("ERROR: " + msg, args...)
}

重新定义类型将限制你可以在其上调用的方法集合为你定义的方法。你将无法在没有先进行类型转换的情况下重用 *Printf 方法。通过嵌入它来包装类型将允许你调用 *Printf 方法,并用你自己的方法包装这些方法。你可以在每种情况下的 Info 和 Error 方法的实现中看到这些示例。

英文:

Both of these answers are correct so I'll throw in a few more alternatives. You can't define methods on a type you didn't define. Functions are indeed one way you can do this but you can also do this by redefining the type or wrapping the type.

For instance if you don't care about splitting the outfile and just want to modify the log line then you can do one of these.

Redefine the type:

type MyLogger log.Logger

func (l MyLogger) Info(msg string, args ...interface{}) {
    log.Logger(l).Printf(msg, args...)
}

func (l MyLogger) Error(msg string, args ...interface{} {
    log.Logger(l).Printf("ERROR: " + msg, args...)
}

Or Wrap the type:

type MyLogger struct {
  log.Logger
}

func (l MyLogger) Info(msg string, args ...interface{}) {
  l.Printf(msg, args...)
}

func (l MyLogger) Error(msg string, args ...interface{}) {
  l.Printf("ERROR: " + msg, args...)
}

Redefining the type will limit the method set you can call on it to the ones you define. You won't be able to reuse the *Printf methods without casting it first. Wrapping the type by embedding it will allow you to call the *Printf methods and wrap those methods with your own. You can see examples of this in the implementations of the Info and Error methods for each case.

答案2

得分: 4

Rputikar在回答你的基本问题时是正确的。你不能在你没有创建的类型上定义新的方法。

那么,一个解决方法是不要纠结于方法,只需编写一个普通函数。这是你想要的方法作为一个函数的写法。

编辑2:正如Spirit Zhang正确指出的,我第一个版本的代码在Lshortfile或Llongfile标志的情况下丢失了行号。这是一个改进:

func LogError(l *log.Logger, v interface{}) {
    l.Output(2, fmt.Sprint("ERROR: ", v))
}

完整的工作示例在playground上:http://play.golang.org/p/MJaKQLt24L

如果你将这段代码放在morelog.go中,你可以将morelog.go放在与你的主程序相同的目录中。或者对于一些小函数,不要费心创建一个单独的文件;只需将LogError和LogWarning代码直接放在与你的主程序相同的文件中。

编辑:使用默认记录器的示例:编辑2:**此示例不适用于Lshortfile或Llongfile。**我将其保留在这里,以便人们可以看到问题。我不知道有没有简单的方法来编写这些函数以使用默认记录器。

我的示例展示了一个适用于任何记录器的LogError函数。如果你只需要为默认记录器记录错误,那么更简单:

func LogError(v interface{}) {
    log.Printf("ERROR: %v", v)
}

甚至可以这样:

func LogError(v interface{}) {
    log.Print("ERROR:", v)
}
英文:

Rputikar is correct on the answer to your basic question. You cannot define new methods on a type you didn't create.

One solution then, is to not get hung up on methods, just write a plain function. Here is the method you wanted written as a function.

Edit2: As Spirit Zhang correctly pointed out, my first version of this code lost the line number in the case of the Lshortfile or Llongfile flags. This is an improvement:

func LogError(l *log.Logger, v interface{}) {
    l.Output(2, fmt.Sprint("ERROR: ", v))
}

Complete working example at playground: http://play.golang.org/p/MJaKQLt24L

If you put this code in morelog.go, you can put morelog.go in the directory with your main program. Or don't bother with a separate file for a couple of small functions; just put the LogError and LogWarning code right in the same file with your main program.

Edit: Example with default logger: Edit2: This Example does not work with Lshortfile or Llongfile. I'll leave it here so people can see the problem. I don't know of an easy way to write these functions to use the default logger.

My example shows a LogError function that works for any logger. If you just need to log errors for the default logger, it's even simpler:

func LogError(v interface{}) {
    log.Printf("ERROR: %v", v)
}

or even,

func LogError(v interface{}) {
    log.Print("ERROR:", v)
}

答案3

得分: 2

你不能覆盖你没有创建的类型上的方法。同样,你也不能在你没有创建的类型上定义新的方法。

根据你的需求,你可以简单地创建两个log.Logger的实例,一个用于警告,一个用于错误。

http://play.golang.org/p/DQjIvk-wfI

package main

import (
    "log"
    "os"
)

func main() {

    errorLogger := log.New(os.Stderr, "ERROR: ", log.LstdFlags)
    warnLogger := log.New(os.Stdout, "WARNING: ", log.LstdFlags)

    errorLogger.Println("Hello, playground")
    warnLogger.Println("Hello, playground")
}
英文:

You can not override the method on a type you didn't create. Similarly you can not define new methods on a type you didn't create.

For your needs you can simply have two instances of log.Logger one for warning and one for error.

http://play.golang.org/p/DQjIvk-wfI

package main

import (
	"log"
	"os"
)

func main() {

	errorLogger := log.New(os.Stderr, "ERROR: ", log.LstdFlags)
	warnLogger := log.New(os.Stdout, "WARNING: ", log.LstdFlags)

	errorLogger.Println("Hello, playground")
	warnLogger.Println("Hello, playground")
}

huangapple
  • 本文由 发表于 2013年1月1日 16:43:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/14109080.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定