2014-08-31 14 views
8

Próbuję napisać otoki ogólnego przeznaczenia do subskrypcji, coś jak:Golang: Czy mogę rzucić do interfejsu chan {}

type Subscriber interface{ 
    Subscribe(addr string) chan interface{} 
} 

Załóżmy, że istnieje biblioteka chcę użyć która ma subskrybować metody w to, ale który używa chan library.Object. Chciałbym móc zrobić coś takiego:

func (s *mySubscriber) Subscribe(addr string) chan interface{}{ 
    ch := make(chan library.Object) 
    library.Subscribe(addr, ch) 
    return chan interface{}(ch) 
} 

Obecnie nie wierzę, że taka obsada jest możliwa. I nie chcę modyfikować podstawowej biblioteki, ponieważ opakowanie powinno być agnostyczne dla implementacji biblioteki.

Widziałem już Is there a way to cast Structs for sending over a channel, ale w takim przypadku można zmodyfikować aplikację w zależności od potrzeb. Tutaj nie może. czy to możliwe? Czy istnieje lepszy sposób?

Jednym z rozwiązań jest przejście do kanału Subskrybuj w kanale ogólnego przeznaczenia i czekanie w nieokreślony sposób na chan library.Object i wywoływanie wszystkiego, co pojawia się na moim ogólnym kanale, ale nie bardzo chciałem, aby wprowadzić inny kanał tylko po to, aby obejść typ rzucony.

+2

Go nie ma pojęcia "cast". Wykonuj konwersje typu hase, wpisz asercje i interfejsy (i przełączniki typów) i to wszystko. Wpisz konwersje najbliżej do przesłania, np. C. Mówienie o rzucaniu niedziałających rzutów zamazuje twój pogląd na problem, który musi zostać rozwiązany przez użycie konwersji typu (nie będzie działać w twojej sytuacji!), Sprytne użycie interfejsów i odpowiednich typów asercji/przełączników może być połączone z odbicie. Większość rzeczy z "ogólnego przeznaczenia" w Go jest skazana na niepowodzenie lub zwalnianie przez klęczenie brodząc w odbiciu. – Volker

Odpowiedz

4

Nie, nie można tego zrobić za pomocą tylko rzutowania. Musisz użyć dodatkowego kanału, o czym już wcześniej pomyślałeś. Na szczęście istnieje już biblioteka pomocników (disclaimer: napisałem to). Chcesz funkcję Wrap.

+1

dane. wstyd. może w przyszłości autorzy rozszerzą przełączanie typów, aby to wspierać. z tablicami i mapami mamy ten sam problem, ale przynajmniej tam można przechodzić przez elementy i wpisywać przełącznik do interfejsu {} jeden po drugim. Powinien być jakiś sposób na zrobienie tego z kanałem, ale może jest jakiś dobry powód? – Ethan

+0

Powodem tego nie jest to, że typy mają zasadniczo różne podstawowe układy pamięci, więc nie jest tak proste, jak "rzutowanie" byłoby w C. Na przykład, [] struct {x, y int} wygląda zupełnie inaczej niż [] interfejs {}, więc nie możesz po prostu "udawać", że to jest jedno, musisz wykonać ręczną konwersję. W przeciwieństwie do prostych rzutów, konwersja może być kosztowna (jak mówisz, musisz przechodzić przez każdy element), więc zdecydowali się jej nie ukrywać, ponieważ nie jest to to samo. – Evan

1

Dla każdego innego, natyka się na ten temat i chce trochę kodu inline:

// wrap a timeout channel in a generic interface channel 
func makeDefaultTimeoutChan() <-chan interface{} { 
    channel := make(chan interface{}) 
    go func() { 
    <-time.After(30 * time.Second) 
    channel <- struct{}{} 
    }() 
    return channel 
} 

// usage 
func main() { 
    resultChannel := doOtherThingReturningAsync() 
    cancel := makeDefaultTimeoutChan() 
    select { 
    case <-cancel: 
     fmt.Println("cancelled!") 
    case results := <-resultChannel: 
     fmt.Printf("got result: %#v\n", results) 
    } 
} 
Powiązane problemy