检查结构体是否实现了给定的接口。

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

Check if struct implements a given interface

问题

我需要遍历一个结构体类型的所有字段,并检查它们是否实现了给定的接口。

type Model interface {...}

func HasModels(m Model) {
    s := reflect.ValueOf(m).Elem()
    t := s.Type()
    modelType := reflect.TypeOf((*Model)(nil)).Elem()

    for i := 0; i < s.NumField(); i++ {
        f := t.Field(i)
        fmt.Printf("%d: %s %s -> %s\n", i, f.Name, f.Type, f.Type.Implements(modelType)) 
    }       
}

然后,如果使用以下结构体调用HasModels:

type Company struct {...}

type User struct {
    ...
    Company Company
}

HasModels(&User{})

Company和User都实现了Model接口,但是对于User结构体的Company字段,f.Type.Implements(ModelType)返回false。

这是意外的结果,所以我在这里做错了什么?

英文:

I need to walk through all the field of a struct type and check if they implements a given interface.

type Model interface {...}

func HasModels(m Model) {
    s := reflect.ValueOf(m).Elem()
    t := s.Type()
    modelType := reflect.TypeOf((*Model)(nil)).Elem()

    for i := 0; i &lt; s.NumField(); i++ {
        f := t.Field(i)
        fmt.Printf(&quot;%d: %s %s -&gt; %s\n&quot;, i, f.Name, f.Type, f.Type.Implements(modelType)) 
    }       
}

Then, if a call HasModels with a struct like so :

type Company struct {...}

type User struct {
    ...
    Company Company
}

HasModels(&amp;User{})

With Company and User both implementing Model; I get f.Type.Implements(ModelType) returning false for the Company field of the User struct.

This is unexpected, so, what Am I doing wrong here ?

答案1

得分: 32

很抱歉,你遗漏了一些关键部分(请始终发布完整的程序),所以我只能猜测问题可能出在指针接收器上定义的方法中,在这种情况下,你的代码的行为是符合预期的。请参考以下示例及其输出:

package main

import (
	"fmt"
	"reflect"
)

type Model interface {
	m()
}

func HasModels(m Model) {
	s := reflect.ValueOf(m).Elem()
	t := s.Type()
	modelType := reflect.TypeOf((*Model)(nil)).Elem()

	for i := 0; i < s.NumField(); i++ {
		f := t.Field(i)
		fmt.Printf("%d: %s %s -> %t\n", i, f.Name, f.Type, f.Type.Implements(modelType))
	}
}

type Company struct{}

func (Company) m() {}

type Department struct{}

func (*Department) m() {}

type User struct {
	CompanyA    Company
	CompanyB    *Company
	DepartmentA Department
	DepartmentB *Department
}

func (User) m() {}

func main() {
	HasModels(&User{})
}

输出:

0: CompanyA main.Company -> true
1: CompanyB *main.Company -> true
2: DepartmentA main.Department -> false
3: DepartmentB *main.Department -> true

Playground

英文:

You've unfortunately left out the essential parts (please always post complete programs), so I can only guess that the problem is in a method defined on a pointer receiver, in which case the behavior of your code is expected. Check this example and its output:

package main

import (
        &quot;fmt&quot;
        &quot;reflect&quot;
)

type Model interface {
        m()
}

func HasModels(m Model) {
        s := reflect.ValueOf(m).Elem()
        t := s.Type()
        modelType := reflect.TypeOf((*Model)(nil)).Elem()

        for i := 0; i &lt; s.NumField(); i++ {
                f := t.Field(i)
                fmt.Printf(&quot;%d: %s %s -&gt; %t\n&quot;, i, f.Name, f.Type, f.Type.Implements(modelType))
        }
}

type Company struct{}

func (Company) m() {}

type Department struct{}

func (*Department) m() {}

type User struct {
        CompanyA    Company
        CompanyB    *Company
        DepartmentA Department
        DepartmentB *Department
}

func (User) m() {}

func main() {
        HasModels(&amp;User{})
}

Playground


Output:

0: CompanyA main.Company -&gt; true
1: CompanyB *main.Company -&gt; true
2: DepartmentA main.Department -&gt; false
3: DepartmentB *main.Department -&gt; true

答案2

得分: 12

有一种更简单的方法可以做到这一点,而不需要使用 reflect。例如:

type middlewarer interface {Middleware() negroni.Handler}
for _, controller := range ctrls {
    if m, ok := interface{}(controller).(middlewarer); ok {
	    n.Use(m.Middleware())
    }
}

只在实现了 middlewarer 接口的切片元素中调用 Middleware() 方法。

英文:

There's an easier way of doing this that doesn't need reflect. For example:

type middlewarer interface {Middleware() negroni.Handler}
for _, controller := range ctrls {
    if m, ok := interface{}(controller).(middlewarer); ok {
	    n.Use(m.Middleware())
    }
}

calls the Middleware() method only in those slice elements that implement the middlewarer interface.

答案3

得分: 2

如果变量不是一个接口类型,可以将其转换为接口类型。

foo := interface{}(yourVar)

然后你可以检查它是否实现了你感兴趣的接口。

bar, ok := foo.(yourInterface)
if !ok {
	// 处理不匹配接口的情况
}
bar.methodOnYourInterface()

这里是一个更详细示例的 Playground 链接

英文:

If the variable is not an interface, make it one.

foo := interface{}(yourVar)

Then you can check if it implements the interface you're interested in.

bar, ok := foo.(yourInterface)
if !ok {
	// handle the case where it doesn&#39;t match the interface
}
bar.methodOnYourInterface()

Here's a playground link for a more fleshed out example

huangapple
  • 本文由 发表于 2013年9月2日 17:57:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/18570391.html
匿名

发表评论

匿名网友

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

确定