2013-01-02 13 views
10

पर ध्यान दिए बिना इंटरफ़ेस पर गतिशील रूप से कॉल विधि {} मैं गो में लिखित एक टेम्पलेटिंग सिस्टम पर काम कर रहा हूं, जिसका अर्थ है कि इसे reflect पैकेज का उदार उपयोग करने की आवश्यकता है। इस विशिष्ट परिस्थिति में मुझे interface{} पर गतिशील रूप से एक विधि कॉल करने में सक्षम होना चाहिए। विचित्रता यह है कि मेरा प्रतिबिंब तर्क तब तक ठीक काम करता है जब तक मेरा डेटा ज्ञात प्रकार का न हो, लेकिन यदि डेटा interface{} प्रकार का है।रिसीवर प्रकार

निम्न उदाहरण आप देख सकते हैं कि main() और Pass() में तर्क समान है। फर्क सिर्फ इतना है डेटा एक ज्ञात प्रकार या एक interface{}

जाओ खेलो अंदर एक ज्ञात प्रकार है कि क्या है: http://play.golang.org/p/FTP3wgc0sZ

package main 

import (
    "fmt" 
    "reflect" 
) 

type Test struct { 
    Start string 
} 

func (t *Test) Finish() string { 
    return t.Start + "finish" 
} 

func Pass(i interface{}) { 
    _, ok := reflect.TypeOf(&i).MethodByName("Finish") 
    if ok { 
     fmt.Println(reflect.ValueOf(&i).MethodByName("Finish").Call([]reflect.Value{})[0]) 
    } else { 
     fmt.Println("Pass() fail") 
    } 
} 

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

    Pass(i) 
    _, ok := reflect.TypeOf(&i).MethodByName("Finish") 
    if ok { 
     fmt.Println(reflect.ValueOf(&i).MethodByName("Finish").Call([]reflect.Value{})[0]) 
    } else { 
     fmt.Println("main() fail") 
    } 
} 

इस कोड को हम निम्न परिणाम

Pass() fail 
startfinish 

पाने को क्रियान्वित करने पर कौन सा इसका मतलब है कि एक विधि को गतिशील रूप से कॉल करने के लिए मेरी पद्धति एक परिदृश्य को छोड़कर ठीक काम करती है जब मेरी ऑब्जेक्ट वर्तमान में interface{} में होती है।

यदि इसके बजाय मैं एक सूचक रिसीवर का उपयोग नहीं करता और i पास करता हूं तो यह अपेक्षा के अनुसार काम करता है।

जाओ खेल: http://play.golang.org/p/myM0UXVYzX

यह मैं विश्वास करने के लिए मेरी समस्या है जब यह एक interface{} है कि मैं मैं का पता (&i) उपयोग नहीं कर सकते है। मैंने प्रतिबिंबित पैकेज और परीक्षण की गई चीजें जैसे reflect.Value.Addr() और reflect.PtrTo() को खराब कर दिया है, लेकिन मैं जिस तरह से आवश्यक था, मैं काम नहीं कर सका। मेरा झुकाव यह है कि इस तथ्य के साथ कुछ करना है कि interface{} परिभाषा एक संदर्भ वस्तु है।

उत्तर

16

@Jeremy दीवार के लिए धन्यवाद मेरा मानना ​​है कि मैं अपने समस्या को हल करने में सक्षम था। मूल मुद्दा interface{} पर गतिशील रूप से नामित विधि को कॉल कर रहा है। 4 मामले हैं

  1. interface{} अंतर्निहित डेटा है मूल्य और रिसीवर मूल्य
  2. interface{} अंतर्निहित डेटा है सूचक और रिसीवर मूल्य है
  3. interface{} अंतर्निहित डेटा मूल्य और रिसीवर सूचक
  4. interface{} है अंतर्निहित डेटा सूचक है और रिसीवर है सूचक

प्रतिबिंब का उपयोग करके हम अपने int के अंडरलिंग मान को निर्धारित कर सकते हैं erface। फिर आगे प्रतिबिंब का उपयोग करके हम वैकल्पिक डेटा प्रकार को हमारे वर्तमान प्रकार में उत्पन्न कर सकते हैं।डेटा में पारित एक मूल्य था अगर हम अब जब हम दोनों डेटा प्रकार हम बस के साथ

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() 
} 

इसलिए प्रत्येक का उपयोग एक मौजूदा विधि के लिए जाँच करने के लिए कर सकते हैं यह

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 
} 

के लिए सूचक उत्पन्न करने के लिए की जरूरत है यह ध्यान में रखते हुए हम *receiver या receiver के रूप में घोषित किए गए किसी भी विधि को गतिशील रूप से कॉल कर सकते हैं।

संकल्पना की

पूर्ण प्रमाण: http://play.golang.org/p/AU-Km5VjZs

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")) 
} 
+0

धन्यवाद। यद्यपि मेरा प्रश्न अलग था, लेकिन आपके उत्तर ने मुझे यह निर्धारित करने में मदद की कि क्या एक संरचना के पास 'IsValid() 'है। – Matt

+0

मैं अपने स्वयं के प्रश्न का उत्तर देने की कोशिश करते समय इस (http://play.golang.org/p/v7lC0swB3s) के साथ आया (http://stackoverflow.com/questions/20167935/can-embedded-methods-access- माता-पिता -खेत) – Brenden

4

अपने उदाहरण में आप फिनिश विधि का समर्थन करने वाले किसी चीज़ के साथ पास नहीं करते हैं क्योंकि फिनिश केवल टेस्ट structs पर पॉइंटर्स पर परिभाषित किया जाता है। MethodByName ठीक उसी तरह कर रहा है जो उस मामले में होना चाहिए। *Test != Test वे दो अलग-अलग प्रकार हैं। प्रतिबिंब की कोई भी राशि एक टेस्ट में एक टेस्ट में बदल जाएगी। और वास्तव में यह भी नहीं होना चाहिए। आप PtrTo फ़ंक्शन का उपयोग यह पता लगाने के लिए कर सकते हैं कि फिनिश विधि को पॉइंटर प्रकार पर परिभाषित किया गया है, लेकिन इससे आपको वास्तविक मान पर पॉइंटर प्राप्त करने में मदद नहीं मिलेगी।

एक सूचक के साथ कॉलिंग पास काम करता है: http://play.golang.org/p/fICI3cqT4t

+1

आपका जवाब खोजने में काफी मददगार था समस्या का जवाब। कुंजी थी 'प्रतिबिंब की कोई राशि एक टेस्ट को एक टेस्ट में बदल नहीं देगी, जिसने मुझे एहसास दिलाया कि समस्या को हल करने के लिए एक रास्ता खोजने पर आराम किया गया। मैंने अपना खुद का जवाब जोड़ा है जो मुझे विश्वास है कि कोई प्रतिबिंब का उपयोग करके 'टेस्ट' से' * टेस्ट 'बना सकता है। यह मुझे रिसीवर प्रकार के बावजूद किसी भी 'इंटरफ़ेस {} 'गतिशील रूप से नाम से किसी भी विधि को कॉल करने की अनुमति देता है। – Nucleon

+0

वास्तव में आप एक नया प्रकार बनाने के लिए प्रतिबिंबित एपीआई का उपयोग कर सकते हैं जो मूल्य के लिए सूचक है और यह काम करेगा। लेकिन आपके अधीन चलने वाले दर्शन के तहत अब आप उसी प्रकार के काम पर काम नहीं कर रहे हैं। किसी भी तरह से खुशी है कि आप हल करने में सक्षम थे आप समस्या हैं। –

0

यह इसी तरह की सुविधा को लागू करने के golang का एक अच्छा उदाहरण है:

package main 

import "fmt" 

type Parent struct { 
    Attr1 string 
} 

type Parenter interface { 
    GetParent() Parent 
} 

type Child struct { 
    Parent //embed 
    Attr string 
} 

func (c Child) GetParent() Parent { 
    return c.Parent 
} 

func setf(p Parenter) { 
    fmt.Println(p) 
} 

func main() { 
    var ch Child 
    ch.Attr = "1" 
    ch.Attr1 = "2" 

    setf(ch) 
} 

कोड से: https://groups.google.com/d/msg/golang-nuts/8lK0WsGqQ-0/HJgsYW_HCDoJ

संबंधित मुद्दे