如何获取一个指向interface{}的指针以检查接口的实现类型

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

How to get a pointer of interface{} to type check interface implementation

问题

所以我基本上想找出实现类似以下代码的最佳方法:

package main

import "fmt"

type SomeStruct struct {
}

type SomeInterface interface {
  SomeMethodWhichNeedsAPointerReceiver() string
}

func (s *SomeStruct) SomeMethodWhichNeedsAPointerReceiver() string {
  return "Well, believe me, I wrote something"
}

func Run(s interface{}) {
  // 在这里如何将 s 转换为指针?
  casted, ok := s.(SomeInterface)
  if ok {
    fmt.Println("Awesome: " + casted.SomeMethodWhichNeedsAPointerReceiver())
    return 
  }

  fmt.Println("Fail :(")
}

func SomeThirdPartyMethod() interface{} {
  return SomeStruct{}
}

func main() {
  x := SomeThirdPartyMethod()
  Run(x)
}

我的问题在于 Run 方法中的类型转换。
我只知道它的类型是 interface{},现在我需要调用一个具有指针接收器的接口方法。

我目前唯一的解决方案是使用反射动态构建一个切片,将元素设置到切片中,然后使其可寻址。

这真的是找到解决方案的唯一可能性吗?

Play链接

英文:

So I'm basically trying to find out the best way to get to something like this:

package main

import "fmt"

type SomeStruct struct {
}

type SomeInterface interface {
  SomeMethodWhichNeedsAPointerReceiver() string
}

func (s *SomeStruct) SomeMethodWhichNeedsAPointerReceiver() string {
  return "Well, believe me, I wrote something"
}

func Run(s interface{}) {
  // how can I cast s to a pointer here?
  casted, ok := (s).(SomeInterface)
  if ok {
    fmt.Println("Awesome: " + casted.SomeMethodWhichNeedsAPointerReceiver())
    return 
  }

  fmt.Println("Fail :(")
}

func SomeThirdPartyMethod() interface{} {
  return SomeStruct{}
}

func main() {
  x := SomeThirdPartyMethod()
  Run(x)
}

My problem here is, at the typecast in the Run method.
I basically only know that it is of type interface{} and now I need
to call an interface method, which has a pointer receiver.

My only solution at the moment is to dynamically construct a slice with reflection, set the element into the slice, and than make it adressable.

Is this really the only possibility to find a solution?

Play link

答案1

得分: 6

在Go语言中,接口只是一组方法的集合(规范:接口类型)。

接口不指定接收者是指针还是非指针。在Go语言中,实现接口是隐式的:没有声明意图。

你需要阅读并理解方法集。引用其中的一个重要部分:

> 任何其他类型 T 的方法集由所有声明了接收者类型为 T方法组成。相应的指针类型 *T 的方法集是由所有声明了接收者为 *TT 的方法组成的(也就是说,它还包含了 T 的方法集)。

一个值(可能是指针,也可能不是指针)实现了一个接口,如果它的类型的方法集是接口的超集(即接口类型的方法)。

在你的例子中:

func (s *SomeStruct) SomeMethodWhichNeedsAPointerReceiver() string{}

SomeMethodWhichNeedsAPointerReceiver() 有一个指针接收者,所以类型为 SomeStruct 的值将没有这个方法,但是类型为 *SomeStruct 的值将有。

因此,类型为 SomeStruct 的值不实现你的 SomeInterface 接口(因为它没有 SomeMethodWhichNeedsAPointerReceiver() 方法),但是 *SomeStruct 实现你的 SomeInterface,因为它的方法集包含了 SomeMethodWhichNeedsAPointerReceiver() 方法。

由于你创建并使用的是一个简单的 SomeStruct 值(而不是指向它的指针),类型断言将失败。

如果你使用了 *SomeStruct,类型断言将会成功。

修改你的 SomeThirdPartyMethod() 函数来创建并返回一个 *SomeStruct(一个指针),它将按照你的期望工作:

func SomeThirdPartyMethod() interface{} {
	return &SomeStruct{}
}

或者作为另一种选择:

func SomeThirdPartyMethod() interface{} {
	return new(SomeStruct)
}

Go Playground上试一试。

如果你不能修改 SomeThirdPartyMethod()

如果你不能修改 SomeThirdPartyMethod() 函数:首先,它是否有意义/需要返回一个实现 SomeStruct 的值?如果是这样,那么它当前的实现是错误的,你可以期望它返回一个实现 SomeStruct 的值,而不需要你对其进行解引用(为了获得一个实现 SomeStruct 的值)。

在这种特殊情况下,你还可以尝试对 SomeStruct 本身进行类型断言:

if ss, ok := s.(SomeStruct); ok {
	fmt.Println(ss.SomeMethodWhichNeedsAPointerReceiver())
}

调用 ss.SomeMethodWhichNeedsAPointerReceiver() 将自动对 ss 进行解引用,以获取其地址(这将作为调用 SomeMethodWhichNeedsAPointerReceiver() 方法的指针接收者值)。

英文:

In Go, an interface is just a set of methods (spec: Interface types).

The interface does not specify whether receivers are pointers or not. Implementing an interface in Go is implicit: there is no declaration of intent.

You need to read and understand Method sets. Quoting an important part of it:

> The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T).

A value (which may or may not be a pointer) implements an interface if the method set of its type is a superset of the interface (the methods of the interface type).

In your example:

func (s *SomeStruct) SomeMethodWhichNeedsAPointerReceiver() string{}

SomeMethodWhichNeedsAPointerReceiver() has a pointer receiver, so a value of type SomeStruct will not have this method, but a value of type *SomeStruct will.

So as a consequence a value of type SomeStruct does not implement your SomeInterface interface (because it does not have a SomeMethodWhichNeedsAPointerReceiver() method), but *SomeStruct does implement your SomeInterface because its method set contains the SomeMethodWhichNeedsAPointerReceiver() method.

Since you create and use a simple SomeStruct value (and not a pointer to it), the type assertion will fail.

Should you have used a *SomeStruct, the type assertion would have succeeded.

Modify your SomeThirdPartyMethod() function to create and return a *SomeStruct (a pointer) and it will work as you expect it:

func SomeThirdPartyMethod() interface{} {
	return &SomeStruct{}
}

Or as an alternative:

func SomeThirdPartyMethod() interface{} {
	return new(SomeStruct)
}

Try it on the Go Playground.

If you can't modify SomeThirdPartyMethod()

If you can't modify the SomeThirdPartyMethod() function: first of all, is it intended/required that it returns a value which implements SomeStruct? If so, then its current implementation is wrong, and you can expect it to return a value which does implement SomeStruct without you having to dereference it (in order to gain a value which implements SomeStruct).

In this specific case you can also try type assertion for SomeStruct itself:

if ss, ok := s.(SomeStruct); ok {
	fmt.Println(ss.SomeMethodWhichNeedsAPointerReceiver())
}

Calling ss.SomeMethodWhichNeedsAPointerReceiver() will automatically dereference ss to take its address (which will be the pointer receiver value for calling the SomeMethodWhichNeedsAPointerReceiver() method).

答案2

得分: 4

最好的解决方案确实是从SomeThirdPartyMethod返回一个指针。但如果这不可能,你可以使用反射构造一个指向该值的指针:

v := reflect.Indirect(reflect.New(reflect.TypeOf(s)))
v.Set(reflect.ValueOf(s))
sp := v.Addr().Interface()
casted, ok := sp.(SomeInterface)
if ok {
    fmt.Println("也可以:", casted.SomeMethodWhichNeedsAPointerReceiver())
    return
}

工作示例:http://play.golang.org/p/JYJT8mRxWN

英文:

The best solution would indeed be to return a pointer from SomeThirdPartyMethod. But if this is not possible, you can construct a pointer to that value using reflection:

v := reflect.Indirect(reflect.New(reflect.TypeOf(s)))
v.Set(reflect.ValueOf(s))
sp := v.Addr().Interface()
casted, ok = (sp).(SomeInterface)
if ok {
	fmt.Println("Good as well: " + casted.SomeMethodWhichNeedsAPointerReceiver())
	return
}

Working example: http://play.golang.org/p/JYJT8mRxWN

huangapple
  • 本文由 发表于 2015年4月20日 17:38:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/29744216.html
匿名

发表评论

匿名网友

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

确定