2016-08-04 2 views
2

저는 최근에 일부 감사 프로세스를 작성했습니다. 이것의 대부분은 임의의 구성을 따로 따로 리플렉션하는 것입니다. 객체 평등을 위해 사용할 수있는 인터페이스가 있습니다. 분리 된 모든 것들이이 인터페이스를 구현하는 것은 아니며 비교는 임의의 유형 (객체 평등은 유형의 평등을 의미하지는 않음) 사이에있을 수 있습니다.어떻게 구현하는지 알고있는 인터페이스에 reflect.Value 구조체를 다시 입력 할 수 있습니까?

비교 대상은 동일한 유형 일 필요가 없기 때문에 무언가가 한쪽면에 포인터이고 다른 쪽 (* 문자열 대 문자열 또는 * ChickenBox 대 BoxOfChicken)의 값이있는 경우가 있습니다. 나는 단순히 포인터/인터페이스 값을 참조 해제했을 때 비교했을 때 비교가 간단합니다.

내 문제는 포인터/인터페이스를 dereferenced 일단 부모 포인터/인터페이스를 구현하는 인터페이스에 어설 션을 입력 할 수없는 것 같습니다. 이 방법을 보여줍니다 : 여기

는 예 ( https://play.golang.org/p/O_dvyW07fu 놀이터) : 인이의

package main 

import (
    "fmt" 
    "reflect" 
) 

// ID interface 
type IHasID interface { 
    GetID() string 
    SetID(id string) 
} 

// implementing type 
type HasID struct { 
    ID string `bson:"_id,omitempty"` 
} 

func (be *HasID) GetID() string { 
    return be.ID 
} 
func (be *HasID) SetID(id string) { 
    be.ID = id 
} 

// static comparison reflect.Type of IHasID 
var idType = reflect.TypeOf((*IHasID)(nil)).Elem() 

func main() { 
    testy := struct { 
     HasID 
     Somefield int 
    }{ 
     HasID{ 
      ID: "testymctest", 
     }, 
     4, 
    } 

    id, _ := GetIDFrom(reflect.ValueOf(testy)) 
    fmt.Printf("ID: %v\n", id) 
} 

// check if the reflect.Value implements IHasID 
func ImplementsID(x reflect.Value) bool { 
    switch x.Kind() { 
    case reflect.Interface, reflect.Ptr: 
     if x.IsValid() && !x.IsNil() { 
      return ImplementsID(x.Elem()) 
     } 
    case reflect.Struct: 
     if x.IsValid() { 
      return reflect.PtrTo(x.Type()).Implements(idType) 
     } 
    } 
    return false 
} 

// check and get the reflect.Value's ID 
func GetIDFrom(x reflect.Value) (string, bool) { 
    switch x.Kind() { 
    case reflect.Ptr, reflect.Interface: 
     if x.IsValid() && !x.IsNil() { 
      return GetIDFrom(x.Elem()) 
     } 
    case reflect.Struct: 
     if x.IsValid() && ImplementsID(x) { 
      // not using val,ok syntax to demo issue, if it gets here it should 
      // implement ID since we did the check in the if statement above 
      val := x.Interface().(IHasID) 
      return val.GetID(), true 
     } 
    } 
    return "", false 
} 

나의 이해는 인터페이스가 두 부분 (https://blog.golang.org/laws-of-reflection) 것입니다. 반영 내에서 이것은 반영에 의해 대표된다. 유형 (광범위하게 방법)과 반영. 가치 (광범위하게 데이터). 라인 (53)에 우리가 reflect.PtrTo 사용하여 인터페이스에 대한 우리의 관계를 재구성 할 수 있기 때문에

if x.IsValid() && ImplementsID(x) { 

이것은 : 데모 내에서 우리는 통과 reflect.Value의 reflect.Type 라인 67의 인터페이스를 구현하는 성공적으로 평가

return reflect.PtrTo(x.Type()).Implements(idType) 

reflect.TYP에서 복제 할 수없는 것은이 프로세스 (reflect.Type 쪽)입니다. 그래서 라인 (70)에 우리는 단순히 (방법이없는) 기본 구조체가이 실패

val := x.Interface().(IHasID) 

를 내가 나를 어떻게 할 수있는 구문을 찾을 수 없었습니다 것은 reflect.Type에 불가능했을 것 같다 측면; assert의 인터페이스를 충족하는 구조체에 대해 정의 된 메소드로 돌아온다. (나는 reflect.Type이 캐시 된 인터페이스 스토어에 대해 간단히 작동하고 structs 메소드가이 시점까지 나에게 손실되었다고 생각한다.)

나는 어떤 작업을하기 전에 단순히 포인터/인터페이스의 존재를 강요하는 실무 솔루션을 가지고 있지만 뭔가를 놓친 것처럼 느껴진다. 그 반사를 감안할 때. 값의 반영. 타입이 인터페이스를 성공적으로 찾을 수 있습니다. 내가 반영 할 수있는 비슷한 것이 확실합니다. 가치면?

답변

1

GetID 메서드에는 포인터 수신기가 필요하지만 구조체 값에서 작업하고 있습니다. 이 값은 주소 지정이 가능하지 않으므로 GetID 메서드를 호출하거나 Addr()을 호출하여 값에 대한 포인터를 가져올 수 없습니다.당신이 먼저해야 GetIDFrom

id, _ := GetIDFrom(reflect.ValueOf(&testy)) 

를 호출 할 때 https://play.golang.org/p/ZPQnVXtx01

hid := HasID{ID: "testID"} 
_ = IHasID(hid) 

당신은 원래 구조체 값에 대한 포인터를 사용해야합니다

이이 컴파일되지 않습니다 같은 이유는 구조체 타입을 확인하기 위해 Elem()으로 역 참조하기 전에 포인터가 인터페이스를 구현하는지 검사한다.

+0

그리고 내가 반영한 것처럼 그 값을 주소 지정 할 수있는 방법이 없습니다. 유형 측면입니까? – lmunro

+0

@Imunro, 아니요,하지만 포인터로 초기 값을 전달하면됩니다. – OneOfOne

+0

@lmunro : 주소를 지정할 수없는 값의 주소를 취할 방법이 없습니다. 'reflect.New'를 사용하여 구조체에 새로운 포인터를 만들고 이전 값을 복사 할 수는 있지만 처음부터 포인터로 시작하는 것이 더 쉽습니다. – JimB