关于编写惯用的 Golang 的建议

2024-05-27

我正在掌握 Golang 的做事方式。首先是一些示例代码:

package main

import (
    "log"
    "os"
)

func logIt(s string) {
    f, _ := os.OpenFile("errors.log", os.O_RDWR|os.O_CREATE|os.O_APPEND,
        0666)
    defer f.Close()

    log.SetOutput(f)
    log.Println(s)
}

type iAm func(string)

func a(iam string) { logIt(iam + " A") }

func b(iam string) { logIt(iam + " B") }

func c(iam string) { logIt(iam + " C") }

var funcs = map[string]iAm{"A": a, "B": b, "C": c}

func main() {
    funcs["A"]("Je suis")
    funcs["B"]("Ich bin")
    funcs["A"]("Yo soy")
    funcs["D"]("Soy Yo")
}

说明

  • 我将所有的注销都存储到一个文件中,以便稍后监控。这是正确的渠道方式吗?
  • 我想根据用户输入确定在运行时调用的正确函数。为此,我将函数打包为 Golang 映射 - 在 PHP 中,我将使用关联数组。这有效。然而,这是一种有效的做事方式吗?
  • 最后,你会注意到我实际上并没有D在我的地图上输入钥匙。最后一个 funcs 调用导致 Go 抛出一个不稳定的错误。在另一种语言中,我会将这些调用包装在 try... 块中并避免该问题。据我了解,Go 哲学是检查密钥的有效性first并恐慌,而不是试图盲目地使用该键。那是对的吗?

我是一个 Go 初学者,所以我可能有我使用的其他语言的包袱。在我看来,以先发制人的方式处理异常情况(在使用密钥之前检查密钥)既不明智也不高效。正确的?


记录到文件

每次我想记录某些内容时,我不会打开和关闭文件。在启动时,我只需打开它一次并将其设置为输出,然后在程序存在之前将其关闭。我不会使用logIt()功能:只需使用以下功能进行记录log https://golang.org/pkg/log/包,这样你就可以进行格式化日志记录,例如和log.Printf() https://golang.org/pkg/log/#Printf etc.

动态功能选择

函数映射完全没问题,并且在性能方面表现良好。如果你需要更快的东西,你可以做switch根据函数名直接调用目标函数case分支机构。

检查密钥是否存在

中的值map是函数值。函数类型的零值是nil你不能打电话给nil函数,因此您必须在继续调用它之前检查该值。请注意,如果您使用不存在的键对映射进行索引,则返回值类型的零值,即nil如果是函数类型。所以我们可以简单地检查该值是否为nil。还有另一种逗号惯用语,例如fv, ok := funcs[name] where ok将是一个布尔值,告诉您是否在地图中找到了该键。

不过,您可以在一个地方完成此操作,而不必在每次调用中重复它:

func call(name, iam string) {
    if fv := funcs[name]; fv != nil {
        fv(iam)
    }
}

Note:

如果您选择使用switch, the default分支将处理无效的函数名称(这里当然不需要函数映射):

func call(name, iam string) error {
    switch name {
    case "A":
        a(iam)
    case "B":
        b(iam)
    case "C":
        c(iam)
    default:
        return errors.New("Unknown function: " + name)
    }
    return nil
}

错误处理/报告

在 Go 中,函数可以有多个返回值,因此在 Go 中,您可以通过返回一个来传播错误error https://golang.org/pkg/builtin/#error值,即使该函数通常有其他返回值。

So the call()函数应该有一个error如果找不到指定的函数,则返回类型以发出信号。

您可以选择退回新的error创造的价值,例如这errors.New() https://golang.org/pkg/errors/#New函数(因此它可以是动态的),或者您可以选择创建一个全局变量并具有固定的错误值,例如:

var ErrInvalidFunc = errors.New("Invalid function!")

该解决方案的优点是调用者call()函数可以比较返回的error的值对的值ErrInvalidFunc全局变量来了解这种情况并采取相应的行动,例如:

if err := call("foo", "bar"); err != nil {
    if err == ErrInvalidFunc {
        // "foo" is an invalid function name
    } else {
        // Some other error
    }
}

所以完整修改后的程序:

(Slightly compacted to avoid vertical scroll bars.)

package main

import ("errors"; "log"; "os")

type iAm func(string)

func a(iam string) { log.Println(iam + " A") }
func b(iam string) { log.Println(iam + " B") }
func c(iam string) { log.Println(iam + " C") }

var funcs = map[string]iAm{"A": a, "B": b, "C": c}

func main() {
    f, err := os.OpenFile("errors.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        panic(err)
    }
    defer f.Close()
    log.SetOutput(f)

    call("A", "Je suis")
    call("B", "Ich bin")
    call("C", "Yo soy")
    call("D", "Soy Yo")
}

func call(name, iam string) error {
    if fv := funcs[name]; fv == nil {
        return errors.New("Unknown funcion: " + name)
    } else {
        fv(iam)
        return nil
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

关于编写惯用的 Golang 的建议 的相关文章

随机推荐

  • 刷新令牌返回无效的授权类型

    我正在尝试刷新访问令牌 基于https docs wso2 com display IS510 Refresh Token Grant https docs wso2 com display IS510 Refresh Token Gran
  • 尝试理解新的关键字

    我试图准确理解 javascript new 关键字的含义 为什么有必要以及何时应该使用它 考虑以下示例 var x new function var self this this myFunction function alert foo
  • 在 Swift 中使用 UIActivityIndi​​catorView 和 UIWebView

    当 url 加载到 WebView 中时 我试图在我的应用程序中向用户显示活动指示器视图 我尝试过使用 Activity startAnimating activity stopAnimating 并尝试将它们放入函数等中 但没有任何运气
  • 包装类型时“在当前范围内找不到类型 T 的方法”

    我正在尝试围绕两种不同类型制作适配器来完成相同的工作 但我无法重写这两种类型 X有一个消耗的方法self因此运行时多态包装器不适用 唯一的选择是静态通用方法 struct X impl X fn f self n i32 println n
  • 可以作为命令行参数传递多少数据?

    在 Linux 下生成进程时可以发送多少字节作为命令行参数 gahooa 推荐了一篇好文章http www in ulm de mascheck various argmax http www in ulm de mascheck vari
  • 对结构体进行 typedef 对其自身有什么影响? [复制]

    这个问题在这里已经有答案了 我在 API 顶部看到过这样的代码 typedef struct SYSTEM SYSTEM 方式SYSTEM之前是未定义的 有谁知道这是做什么的 编译器认为什么SYSTEM这条线之后 感谢您的回答 我的问题是
  • 有什么理由不将 pdb 与您的应用程序一起提供吗?

    既然您可以使用 Reflector 对 Net 应用程序进行逆向工程 那么有什么理由不随应用程序一起发送 pdb 文件呢 如果您确实将它们一起发送 那么您的堆栈跟踪将包括出现问题的行号 这在崩溃时很有用 每条评论请仅输入 1 个投票理由 传
  • 添加验证码到 Symfony2 登录

    我需要将验证码添加到我的登录页面 我正在使用 GregwarCaptchaBundle 和 FosUserBundle 目前我已经使用以下代码在登录时显示验证码
  • Rails 3 应用程序的 MySQL Cluster (NDB) 与 MySQL Replication (InnoDB):优点/缺点?

    我们正在对当前系统进行概述 试图找出是否可以提高性能和可靠性 目前 我们运行着一堆内部 Rails 应用程序和基于 Rails 的网站 有些已经是 Rails 3 有些正在转换为 Rails 3 它们都连接到以下 MySQL 设置 mysq
  • 如何在jenkins中使用文件参数

    我正在詹金斯中执行参数化构建来计数 有 1 个文件参数的文件中的行数 它的文件位置是pqr 脚本文件的名称是linecount sh保存在远程服务器上 当我尝试使用命令执行它时sh linecount sh文件名 它在詹金斯中完美运行 但是
  • Asynctask:将两个或多个值从 doInBackground 传递到 onPostExecute

    我有一个 Asynctask 它检索两个 int 值 我想将它们传递给 onPostExecute 以在视图上显示它们 这是我的代码 public class QueryServer extends AsyncTask
  • 在不同类的方法之间“共享”变量的正确方法是什么?

    我试图在不同类的方法之间共享变量 但我不知道我是否以正确的方式这样做 基本上 当我想在method2上使用变量时 我必须将它们从A类通过method1 传输 到method2 只需看一下示例 因为我不知道如何正确解释这一点 这是正确的方法吗
  • ARC 项目中出现“Missing [super dealloc]”警告

    我已经将一个项目重构为 ARC 看起来不错 但是有一个对象使用通知中心 我在自定义的 dealloc 方法中删除了观察者 这在非 ARC 项目中效果很好 它也适用于 ARC 但我收到一个疯狂的警告 方法可能缺少 super dealloc
  • 在 UIScrollView 中滚动而不触发 TouchCancelled

    Overview 我正在开发一款 iPhone 游戏 其代码是从另一位开发人员那里继承的 游戏网格是一个 contentSize 为 1000x1000 的 UIScrollView 网格包含通过 OpenGL 的 Texture2D 类在
  • C++ QT libXL 错误:“启动期间程序退出,代码为 0xc0000135”

    我正在尝试编写一个使用 libXL 的 QT 应用程序 但是当我尝试编译时 我收到一个弹出框 显示 During Startup program exited with code 0xc0000135 我已经准确地找出了哪一行导致了问题 它
  • iPhone切换按钮的实现

    我想在我的 iPhone 应用程序中创建一个切换按钮 但是 我不知道最好的方法是什么 我正在考虑两种选择 我可以对 UIButton 进行子类化 这样我就不必实现触摸处理 我可以创建一个返回一个布尔值的方法 指示按钮是处于打开还是关闭状态
  • Java ServiceExecutor 终止条件

    我对 java 执行器很陌生 我正在使用 Java 的 ExecutorService 启动多个线程来处理数据 Executor executor Executors newFixedThreadPool poolSize for int
  • scipy cdist 与稀疏矩阵

    我需要计算两组向量之间的距离 source matrix and target matrix 我有以下几行 当两者source matrix and target matrix属于类型scipy sparse csr csr matrix
  • json 网络前导零(禁用基本转换)

    Json Net 无法正确反序列化带有前导零的数字 例如 number 010 被识别为 8 因为0108 基等于8以 10 为基数 如果看JsonTextReader ParseNumber 你可以看到 long value2 text2
  • 关于编写惯用的 Golang 的建议

    我正在掌握 Golang 的做事方式 首先是一些示例代码 package main import log os func logIt s string f os OpenFile errors log os O RDWR os O CREA