有没有一种标准的方法来对自定义错误类型进行分类?

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

Is there a standard way to categorize custom error types

问题

有没有一种被接受的模式可以将自定义错误类型分组,以便我可以根据它们的分组来进行不同的处理?

例如,在C#中,我会这样做:

abstract class FriendlyException : ApplicationException {
    protected FriendlyException(string message) : base(message) { }
}
class MyNiceException : FriendlyException {
    public MyNiceException(string message) : base(message) { }
}

然后,根据错误类型的不同,在catch块中执行不同的操作:

try { DoSomething(); }
catch (FriendlyException ex) { Console.WriteLine(ex.Message); }
catch { Console.WriteLine("Unhandled error"); }

在Go语言中,我看到可以使用As或直接在类型上进行switch,但由于存在隐式接口,我没有看到可以在没有浪费接口成员的情况下完成这个操作,像这样:

type FriendlyError interface { SomeWastedMethod() }
type MyFriendlyError struct {}
func (fe MyFriendlyError) SomeWastedMethod() {}
func (fe MyFriendlyError) Error() string { return "implementing error interface" }

然后,我可以这样检查错误是否属于该组:

var fe FriendlyError
err := DoSomething()
if err != nil {
    if errors.As(err, &fe) {
        fmt.Println(err.Error())
    } else {
        fmt.Println("Unhandled error")
    }
}

我对MyFriendlyError结构中的那个浪费的方法感到困扰。这是唯一的方法吗?还是还有其他标准模式?

在我的示例中,我希望有多个不同的错误类型,其中一些错误类型可以安全地将消息返回给调用者,而另一些错误类型则只想给调用者一个通用的消息,同时仍保留内部日志的详细信息。

英文:

Is there an accepted pattern for grouping custom error types together so that I can handle them differently according to their group?

For example, here's what I would do in C# with exceptions:

abstract class FriendlyException : ApplicationException {
    protected FriendlyException(string message) : base(message) { }
}
class MyNiceException : FriendlyException {
    public MyNiceException(string message) : base(message) { }
}

I could then do something different in the catch depending on whether or not it falls into that category:

try { DoSomething(); }
catch (FriendlyException ex) { Console.WriteLine(ex.Message); }
catch { Console.WriteLine("Unhandled error"); }

In Go, I see that I can use As or switch directly on the type, but since there are implicit interfaces, I'm not seeing that this can be done without a wasted interface member, like this:

type FriendlyError interface { SomeWastedMethod() }
type MyFriendlyError struct {}
func (fe MyFriendlyError) SomeWastedMethod() {}
func (fe MyFriendlyError) Error() string { return "implementing error interface" }

I could then check to see if an error is of that group like this:

var fe FriendlyError
err := DoSomething()
if err != nil {
    if errors.As(err, &fe) {
	    fmt.Println(err.Error())
    } else {
	    fmt.Println("Unhandled error")
    }
}

That wasted method in the MyFriendlyError struct is what bothers me. Is that the only way to accomplish this, or is there some other standard pattern?

In my example, I would want multiple different error types, some of which are safe to return the message to the caller, and some of which I want to just give the caller a generic message, while still retaining the details for internal logging.

答案1

得分: 1

我建议以下方式:

package main

import (
	"errors"
	"fmt"
)

var (
	ErrInvalidMsg   = errors.New("无效的消息")
	ErrInvalidParam = errors.New("无效的参数")
	ErrBadMsg       = errors.New("错误的消息")
)

type GroupError1 struct {
	Base error
}

func (ge GroupError1) Error() string {
	return ge.Base.Error()
}

type GroupError2 struct {
	Base error
}

func (ge GroupError2) Error() string {
	return ge.Base.Error()
}

func TheError(n int) error {
	switch n % 3 {
	case 0:
		return GroupError2{
			Base: ErrBadMsg,
		}
	case 1:
		return GroupError1{
			Base: ErrInvalidParam,
		}
	case 2:
		return GroupError1{
			Base: ErrInvalidMsg,
		}
	}

	return nil
}

func main() {
	if err := TheError(1); err != nil {
		var ge1 GroupError1
		var ge2 GroupError2

		if ok := errors.As(err, &ge1); ok {
			fmt.Println("错误组 1")
		} else if ok := errors.As(err, &ge2); ok {
			fmt.Println("错误组 2")
		} else {
			fmt.Println("没有错误组")
		}
	}
}

在这里,我定义了两个错误组,因此我们可以使用 errors.As 来检测它们,你不需要定义任何未使用的接口,但你需要手动创建它们,而且没有自动分组。

英文:

I suggest the following way:

package main

import (
	"errors"
	"fmt"
)

var (
	ErrInvalidMsg   = errors.New("invalid message")
	ErrInvalidParam = errors.New("invalid param")
	ErrBadMsg       = errors.New("bad message")
)

type GroupError1 struct {
	Base error
}

func (ge GroupError1) Error() string {
	return ge.Base.Error()
}

type GroupError2 struct {
	Base error
}

func (ge GroupError2) Error() string {
	return ge.Base.Error()
}

func TheError(n int) error {
	switch n % 3 {
	case 0:
		return GroupError2{
			Base: ErrBadMsg,
		}
	case 1:
		return GroupError1{
			Base: ErrInvalidParam,
		}
	case 2:
		return GroupError1{
			Base: ErrInvalidMsg,
		}
	}

	return nil
}

func main() {
	if err := TheError(1); err != nil {
		var ge1 GroupError1
		var ge2 GroupError2

		if ok := errors.As(err, &ge1); ok {
			fmt.Println("error group 1")
		} else if ok := errors.As(err, &ge2); ok {
			fmt.Println("error group 2")
		} else {
			fmt.Println("no error group")
		}
	}
}

Here I defined two error groups, so we can detect them with errors.As and you don't need to define any unused interface, but you need to create them by hand and there isn't any automatic grouping.

huangapple
  • 本文由 发表于 2022年11月13日 12:47:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/74418529.html
匿名

发表评论

匿名网友

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

确定