英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论