2011-11-02 7 views
13

pakiet używam, gosqlite, ma metodę z parametrem o zmiennej liczbie argumentów gdzie jego typ jest pusty interfejs.Przełęcz kawałek ciąg o zmiennej liczbie argumentów pusty parametr interface

func (s *Stmt) Exec(args ...interface{}) os.Error 

mogę nazwać to w porządku, jeśli wyraźnie przechodzą poszczególne parametry:

statement := blah() 
error := statement.Exec("hello", 3.0, true) // works fine 

Jednak jako parametr o zmiennej liczbie argumentów odpowiada zastępcze zasięgu operatora in z moja instrukcja SQL na select liczba tych zastępczych jest nieznany w czasie kompilacji, ale dynamicznie zmienia się w czasie wykonywania w zależności od tego, co robi użytkownik. Na przykład. I skończyć z SQL podobny do poniższego jeśli użytkownik wprowadzi cztery wartości:

SELECT * FROM sky WHERE name IN (?,?,?,?) 

Więc naturalnie chciałbym wywołać metodę Exec z plasterkiem ciągi:

var values []string = getValuesFromUser() 
statement := createStatementWithSufficientNumberOfPlaceholders(len(values)) 
_ := statement.Exec(values...) // compiler doesn't like this 

nie skompilować . mogę ominąć ten problem poprzez stworzenie pusty kawałek interfejsu i kopiowanie referencje na:

values2 := make([]interface{}, len(values)) 
for index, value := range values { values2[index] = value } 
_ := statement.Exec(values2...) // compiler happy but I'm not 

I to działa dobrze, ale czuje się nieco niezgrabne. Zastanawiam się, czy istnieje jakiś trik, aby móc przekazać values bezpośrednio do tej funkcji, lub, w przeciwnym razie, lepszy sposób konwersji wycinka łańcucha na pusty interfejs?

Wielkie dzięki.

Odpowiedz

9

Nie ma sposobu przekazania []string bezpośrednio do parametru ...interface{}. Wykonanie tego wymaga liniowej kopii czasu (z przydziałami n + 1!). Jeśli język ukryłby to przed tobą, byłby to znaczny ukryty koszt. Zwykle przekazywanie plasterka do argumentu wariasu po prostu przekazuje plaster do funkcji.

Jeśli chodzi o inne sposoby robienia tego, możesz uczynić go czystszym, pisząc funkcję, która pobiera []string i zwraca odpowiadającą []interface{}. Oczywiście, będziesz musiał napisać to ponownie dla każdej konwersji, którą chcesz wykonać, ale jest to raczej krótka funkcja, a wszystko, co się zmienia, to podpis. Można użyć reflection, która pochodzi z nieodłączną kosztów wykonywania, aby funkcja „rodzajowe”, takich jak:

valuesVal := reflect.ValueOf(values) 
... 
for i := range values2 { values2[i] = valuesVal.Index(i).Interface() } 
+0

Opuściłem kod konwersji w tej chwili: funkcja wydaje się przesadna, gdy ją dodałem. Domyślam się, że problem polega na tym, że Go nie obsługuje kowariancyjnych tablic/plasterków w sposób, w jaki jestem przyzwyczajony do Javy i C#, co jest prawdopodobnie [nie jest to złe] (http://en.wikipedia.org/wiki /Covariance_and_contravariance_(computer_science)#Arrays_in_C.23_and_Java). –

1

nie mam odpowiedzi. I nie sądzę, że jest jednym ponieważ nawet wbudowane i zmiennej liczbie argumentów kopia i dołączyć mieć taką samą (lub kompatybilny beton) typ elementu „bałwan”, ale mam dwie wyraźne sugestie:

  • nie wracaj []string z getValuesFromUser() (czyli przejść jeszcze ozdób []interface{}),
  • na inny rodzaj końcowego opasania wzywa do statement.Exec() z func podejmowania []string do []interface{} konwersji.

Lub na tej samej, trzeciej, oczywistej nucie rozszerzającej type statement z Exec(args ...string).

P.S. Nie dokonałem żadnych testów porównawczych, ale nie sądzę, że ten rodzaj konwersji jest bardzo kosztowny, ponieważ interfejs {} jest jak typ referencyjny, a kompilator prawdopodobnie wykonuje jakieś brudne figle za zasłoną ... znowu może nie, chociaż , Byłbym też szczęśliwy, gdyby dowiedziałem się o prawdziwym rozwiązaniu.

+0

Dzięki za sugestie. Jednak myślę, że byłoby źle przekazać '[] interface {}' z 'getValuesFromUser()', niż byłoby zapłacić cenę kopii. Jeśli chodzi o rozszerzenie typu: jest to ciekawy pomysł, jednak z zastrzeżeniem, że Go nie obsługuje przeciążonych funkcji, więc musiałby mieć inną nazwę. –

+0

@PaulRuane nie chce brzmieć zbyt filozoficznie, ale nie widzę, jak to by było nie tak, o ile to pozostanie ukryte przed użytkownikiem końcowym (zawsze możesz wpisać cast/assert na przyszłość). na podobny temat jest też starsza [sugestia] (http://stackoverflow.com/questions/3839335/any-sensible-solution-to-the-lack-of-array-slice-covariance-in-go/5370177# 5370177) autorstwa SteveMcQuark, który uznałem za interesujący. – ypb