2010-03-14 23 views
6

Sto estendendo il controllo ImageBox da EmguCV. La proprietà Image del controllo può essere impostata su qualsiasi cosa che implementa l'interfaccia IImage.Metodi di chiamata C# nelle classi generiche

Tutti i seguenti implementare questa interfaccia:

Image<Bgr, Byte> 
Image<Ycc, Byte> 
Image<Hsv, Byte> 

Ora voglio chiamare il metodo Draw sull'oggetto del tipo sopra (che cosa mai possa essere).

Il problema è quando accedo alla proprietà Immagine, il tipo restituito è IImage. IImage non implementa il metodo Draw, ma fa tutto quanto sopra.

Credo di poter trasmettere l'oggetto di tipo IImage a uno dei precedenti (quello di destra) e posso accedere al metodo Draw. Ma come faccio a sapere qual è quella giusta? Se hai un modo migliore per farlo, per favore suggeriscilo pure.

EDIT

dispiace, ma ho dimenticato di dire un pezzo importante di informazioni. Il metodo Draw per ognuna delle classi precedenti prende un argomento diverso. Ad es. Draw per Image<Bgr, Byte> prende un argomento di tipo Bgr e lo Draw per Image<Hsv, Byte> prende un argomento di tipo Hsv invece.

+0

È possibile accedere all'Immagine e al codice immagine? –

+0

EmguCV è un progetto open source, anche se in realtà preferisco semplicemente usare i loro binari :) – Aishwar

+0

Non sembra che l'interfaccia immagine/implementazione sia ben progettata. –

risposta

0

Che ne dite di qualcosa di simile:

void foo(IImage image) 
{ 
    if (image is Image<Bgr, Byte>) 
    { 
     ((Image<Bgr, Byte>)(image)).Draw(); 
    } 

    // handle the other types the same way. 
} 
+3

Penso che sia meglio avere un'implementazione invece di molti ifs. –

+2

Questo è solo l'elemosina per il codice sciatto. Ora per ogni tipo dovrai aggiungere il codice in più posizioni. Sarebbe probabilmente meglio estrapolare questa logica e tutta la logica per i diversi tipi di immagine, in una sorta di renderer piuttosto che fare qualcosa del genere. –

+0

Sono d'accordo che ci sono altre buone risposte e questo in generale può portare a codice molto sciatto. Ma nel mio caso, devo solo farlo una volta e non voglio modificare i file sorgente di EmguCV. Ho usato questo, quindi contrassegnerò questo come accettato. – Aishwar

1

Se ho capito bene - implementa classe Immagine metodo draw. Non so come si sta andando ad usarlo, ma si può fare in questo modo:

private void DrawImage<T1, T2>(Image<T1, T2> image) 
{ 
    image.Draw(); 
} 

Il problema principale su cui sopra è che il chiamante del metodo di cui sopra dovrebbe specificare il T1 e T2 tipi.

Se non si ha il controllo dell'interfaccia di IImage e delle sue implementazioni, penso che si sceglierà tra due soluzioni: specificare i tipi T1 e T2 o avere if/switch con tutte le possibili implementazioni di IImage.

Se si ha accesso all'interfaccia IImage e alla classe Image, è necessario aggiungere l'interfaccia generica IImage e aggiungere il metodo Draw ad esso.

+0

in questo modo sono necessari metodi diversi per diversi tipi generici. – JohnIdol

+0

Perché? Basta chiamare il metodo sopra con i parametri di tipo necessari. –

0

Se sei sicuro che l'Immagine < T, U > che avrete (indipendentemente dal tipo) avranno il metodo di disegno, è possibile utilizzare la riflessione e incidere intorno ad esso:

farei come un metodo di estensione:

public static void Draw(this IImage image) 
{ 
    var method = image.GetType().GetMethod("Draw"); 
    if(method != null) 
    { 
     method.Invoke(image,null); 
    } 
} 

un po 'di hack, ma poi di nuovo, direi che l'API non è progettato correttamente. Ci dovrebbe essere almeno una classe Drawer o qualcosa che sappia come agire su un'immagine.

+0

Questo sarebbe molto lento. –

+0

Ottimizzazione prematura molto? – BFree

+0

No, le riflessioni sono lente e le soluzioni proposte non sono un modo naturale di utilizzare i riflessi. –

1

Dovresti avere un'interfaccia condivisa che aggiunge Draw().

3

Aggiungi un IDrawableImage che eredita da IImage.

public interface IDrawableImage : IImage 
{ 
    void Draw(object val); 
    void Draw(); 
} 

Poi si può semplicemente effettuare le seguenti operazioni:

var drawableImage = container.Image as IDrawableImage; 
if (drawableImage != null) 
    drawableImage.Draw(); 

per abbinare il vostro chiarimento di cui sopra:

public interface IDrawableImage<T, B> : IDrawableImage where B : byte 
{ 
    void Draw<T>(T val); 
} 

poi se si conosce il tipo:

var value = new Hvr(); 
var drawableImage = container.Image as IDrawableImage<Hvr, Byte>; 
if (drawableImage != null) 
    drawableImage.Draw(value); 

se non si

var value = new Hvr(); 
var drawableImage = container.Image as IDrawableImage; 
if (drawableImage != null) 
    drawableImage.Draw(value); 
+2

È consigliabile creare interfacce per questo tipo di problemi. Il controllo di ogni implementazione (come nell'esempio di Adel) viola il principio Open/closed (http://en.wikipedia.org/wiki/Open/closed_principle) e crea un elevato accoppiamento (http://en.wikipedia.org/wiki/Coupling_ % 28computer_science% 29). Con questa interfaccia, puoi creare un nuovo 'Image ' e usarlo senza alterare alcun codice che chiama il metodo 'Draw'. – Ronald

+0

Questo è esattamente il motivo per cui ho suggerito questo metodo, facendo le dichiarazioni di grandi dimensioni se mi ha morso nella parte posteriore più di quanto possa contare nel corso degli anni, specialmente quando inizi a farlo ovunque. All'improvviso hai bisogno di un articolo interno su wiki su come aggiungere un nuovo tipo perché tocchi così tanto codice. –

0

Nella tua domanda manca un'informazione fondamentale: il codice che desideri scrivere.

Abbiamo bisogno di sapere come intendete scrivere il codice che chiama il metodo Draw(...).

0

Oppure, è possibile utilizzare metodi dinamici e rischiare.

Problemi correlati