값과 포인터 수신기를 모두 처리하면서 Go의 인터페이스에서 메서드를 동적으로 호출하려면 어떻게 해야 합니까?

Susan Sarandon
풀어 주다: 2024-11-27 18:35:15
원래의
266명이 탐색했습니다.

How Can I Dynamically Invoke Methods on Interfaces in Go, Handling Both Value and Pointer Receivers?

인터페이스에서 메서드를 동적으로 호출

도전

템플릿 시스템은 Go의 Reflect 패키지에 크게 의존합니다. 이 패키지로 작업할 때 인터페이스에서 메서드를 동적으로 호출하는 데 어려움을 겪을 수 있습니다. 이 문제는 데이터 유형이 인터페이스로 저장될 때 명백해집니다{}.

개념적 이해

인터페이스에 대한 메서드 호출의 역학을 이해하는 것이 중요합니다. 고려해야 할 네 가지 시나리오는 다음과 같습니다.

  • 값 수신자 메서드로 값을 보유하는 인터페이스
  • 값 수신자 메서드로 포인터를 보유하는 인터페이스
  • 값을 보유하는 인터페이스 포인터 수신기 방식
  • 포인터 수신기로 포인터를 잡는 인터페이스 방법

리플렉션은 인터페이스의 기본 데이터 값을 결정하는 데 도움이 될 수 있습니다. 이 정보를 사용하면 대체 데이터 유형을 생성하고 값과 포인터 수신 방법을 구별할 수 있습니다.

해결책

문제를 해결하려면 값과 포인터 표현을 모두 생성해야 합니다. 데이터 유형:

value := reflect.ValueOf(data)
if value.Type().Kind() == reflect.Ptr {
    ptr = value
    value = ptr.Elem() // acquire value referenced by pointer
} else {
    ptr = reflect.New(reflect.TypeOf(i)) // create new pointer
    temp := ptr.Elem() // create variable to value of pointer
    temp.Set(value) // set value of variable to our passed in value
}
로그인 후 복사

두 데이터 유형을 모두 사용하여 메소드 존재 여부를 확인하는 방법은 다음과 같습니다. 간단함:

var finalMethod reflect.Value
method := value.MethodByName(methodName)
if method.IsValid() {
    finalMethod = method
}
// check for method on pointer
method = ptr.MethodByName(methodName)
if method.IsValid() {
    finalMethod = method
}

if (finalMethod.IsValid()) {
    return finalMethod.Call([]reflect.Value{})[0].String()
}
로그인 후 복사

이 접근 방식을 수용하면 값 또는 포인터 수신기로 정의되었는지 여부에 관계없이 모든 메서드를 동적으로 효과적으로 호출하는 것이 가능해집니다.

데모

package main

import (
    "fmt"
    "reflect"
)

type Test struct {
    Start string
}

// value receiver
func (t Test) Finish() string {
    return t.Start + "finish"
}

// pointer receiver
func (t *Test) Another() string {
    return t.Start + "another"
}

func CallMethod(i interface{}, methodName string) interface{} {
    var ptr reflect.Value
    var value reflect.Value
    var finalMethod reflect.Value

    value = reflect.ValueOf(i)

    // if we start with a pointer, we need to get value pointed to
    // if we start with a value, we need to get a pointer to that value
    if value.Type().Kind() == reflect.Ptr {
        ptr = value
        value = ptr.Elem()
    } else {
        ptr = reflect.New(reflect.TypeOf(i))
        temp := ptr.Elem()
        temp.Set(value)
    }

    // check for method on value
    method := value.MethodByName(methodName)
    if method.IsValid() {
        finalMethod = method
    }
    // check for method on pointer
    method = ptr.MethodByName(methodName)
    if method.IsValid() {
        finalMethod = method
    }

    if (finalMethod.IsValid()) {
        return finalMethod.Call([]reflect.Value{})[0].Interface()
    }

    // return or panic, method not found of either type
    return ""
}

func main() {
    i := Test{Start: "start"}
    j := Test{Start: "start2"}

    fmt.Println(CallMethod(i, "Finish"))
    fmt.Println(CallMethod(&i, "Finish"))
    fmt.Println(CallMethod(i, "Another"))
    fmt.Println(CallMethod(&i, "Another"))
    fmt.Println(CallMethod(j, "Finish"))
    fmt.Println(CallMethod(&j, "Finish"))
    fmt.Println(CallMethod(j, "Another"))
    fmt.Println(CallMethod(&j, "Another"))
}
로그인 후 복사

출력:

startfinish
startfinish
<nil>
startanother
startfinish
startfinish
<nil>
startanother
로그인 후 복사

위 내용은 값과 포인터 수신기를 모두 처리하면서 Go의 인터페이스에서 메서드를 동적으로 호출하려면 어떻게 해야 합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿