Golang在接口指针上调用方法

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

Golang calling methods on interface pointer

问题

我正在使用Gorp进行数据库访问,Gorp有一个标准的DbMap类型,以及一个Transaction类型,用于在需要回滚时使用。这两种类型都实现了SqlExecutor接口。

因此,我正在针对SqlExecutor接口进行编程,这样我就可以在不需要更改代码的情况下使用事务。

然后,我初始化一个Gorp的DbMapTransaction,并将其作为字段属性传递进去。问题是,我需要一个指向Gorp "对象"的指针,否则如果Gorp "对象"是按值传递而不是按引用传递,我将无法回滚事务。

当我尝试使用我的代码时,我会得到以下消息:

r.Gorp.Get undefined (type *gorp.SqlExecutor has no field or method Get)

我该如何调用这些方法?

以下是一个代码示例。

package repositories

import (
	"github.com/coopernurse/gorp"
)


type Repository struct {
	Gorp *gorp.SqlExecutor // <<<< 需要指针以便我可以回滚
}

func (r *Repository) GetById(i interface{}, key interface{}) interface{} {
	obj, err := r.Gorp.Get(i, key)
	if err != nil {
		panic(err)
	}
	return obj
}
英文:

I am using Gorp for database access, Gorp has a standard DbMap type, as well as a Transaction type for when you want to be able to roll back. Both types implement the SqlExecutor interface.

I am therefore programming against the SqlExecutor interface, so I can use transactions if I want without needing to change my code.

I then initialise a Gorp DbMap or Transaction and pass it in as a field property. The problem is that I need a pointer to the Gorp "object", else how will I be able to roll back if I use a Transaction if the Gorp "object" is passed by value instead of reference.

I then get a message such as

r.Gorp.Get undefined (type *gorp.SqlExecutor has no field or method Get)

when I try use my code. How do I call the methods?

A code sample is below.

package repositories

import (
	&quot;github.com/coopernurse/gorp&quot;
)


type Repository struct {
	Gorp *gorp.SqlExecutor // &lt;&lt;&lt;&lt; Need pointer so I can rollback
}

func (r *Repository) GetById(i interface{}, key interface{}) interface{} {
	obj, err := r.Gorp.Get(i, key)
	if err != nil {
		panic(err)
	}
	return obj
}

答案1

得分: 21

也许你在过度思考这个问题,或者你可能仍然受到另一种语言中"按引用调用"的影响:

  1. gorp.SqlExecutor 是一个接口,你永远不会使用指向接口值的指针。绝对不会。(从技术上讲,这是不准确的,但如果你真的需要一个指向接口的指针,你已经掌握了足够的Go知识,能够理解为什么"绝对不会"是一个非常好的建议。)
  2. 不要考虑"按引用调用"。Go语言中没有这样的概念。将指针传递给函数并不是"按引用调用"。抛弃这个概念。

我猜你没有尝试在非指向接口的代码上使用事务和回滚?

背景:在Go语言中,你传递指针是因为两个原因:

1)你想要这样做,因为你的结构体非常大,你想避免复制,或者

2)你需要这样做,因为被调用者想要修改原始值(这在具有指针接收器的方法中很常见)。

现在,接口值非常小(只有两个字),所以原因1,传递指向接口值的指针,不适用。原因2在大多数情况下也不适用,因为传递指向接口值的指针将允许你修改接口值本身,但通常你希望修改存储在接口值内部的值。接口值内部存储的值通常是一个指针值,它允许通过调用包装指向该结构体的指针的接口值的方法来更改结构体的值。这听起来很复杂,但实际上并不复杂:初学者不使用指针来操作接口(因为这没有任何好处),有经验的Go程序员也不使用指针来操作接口(因为它没有太多好处),除非他需要修改接口值,通常是在反射期间。

英文:

Maybe you are overthinking the problem, or you may still be under a "call by reference" influence from another language:

  1. gorp.SqlExecutor is an interface and you will never use a pointer to an interface value. Never ever. (Technically this is untrue, but if you actually need a pointer to an interface, you will have mastered Go enough to understand why "never ever" is a really good advice.)
  2. Never think about "call by reference". There is no such thing in Go. Passing a pointer to a function is not "call by reference". Leave that behind.

I assume you did not try to use transactions and do rollbacks on the non-pointer-to-interface code?

Background: In Go, you pass around a pointer to something because of two reasons:

  1. You want to, because your struct is really large and you want to avoid copying, or

  2. you need to, because the callee wants to modify the original (this is typical for methods with a pointer receiver).

Now an interface value is really tiny (just two words) so reason 1, to pass a pointer to an interface value, does not apply. Reason 2 does not apply in most cases as passing a pointer to an interface value will allow you to change the interface value itself, but most often you would like to modify the value stored inside the interface value. This value stored inside the interface value often is a pointer value which allows to change the value of a struct by calling methods on an interface value which wraps a pointer to this struct. This sounds complicated but isn't: The novice Go programmer just doesn't use pointers to interfaces (as this won't do any good) and the experienced Go programmer doesn't use pointers to interfaces (as it won't do much good) unless he needs to modify an interface value, typically during reflection.

答案2

得分: 9

  1. gorp.SqlExecutor 是一个接口,你永远不会使用指向接口值的指针。绝对不要这样做。

  2. 如果这是你的目标,你的方法是错误的。接口是一个包装器,它保证了行为。如果你的接口要求方法改变实现类型,那么实现类型应该是一个指针。指向接口本身的指针不会使实现类型变为可变的。

  3. 你的结构体应该是:

     type Repository struct {
         Gorp gorp.SqlExecutor 
     }
    
     func (r *Repository) GetById(i interface{}, key interface{}) interface{} {
         obj, err := r.Gorp.(gorp.SqlExecutor).Get(i, key)
         if err != nil {
             panic(err)
         }
         return obj
     }
    
英文:
  1. gorp.SqlExecutor is an interface and you will never use a pointer
    to an interface value. Never ever.

  2. If this is your goal, you're going about it incorrectly. An
    interface is a wrapper, and a contract that guarantees behavior. If
    your interface requires that the methods mutate the implementing
    type, then the implementing type should be a pointer. A pointer to
    the interface itself will not suddenly make the implementing type
    mutable.

  3. Your struct should be

    type Repository struct {
        Gorp gorp.SqlExecutor 
    }
    
    
    func (r *Repository) GetById(i interface{}, key interface{}) interface{} {
        obj, err := r.Gorp.(gorp.SqlExecutor).Get(i, key)
        if err != nil {
            panic(err)
        }
        return obj
    }
    

huangapple
  • 本文由 发表于 2014年1月22日 20:17:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/21282741.html
匿名

发表评论

匿名网友

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

确定