2010-04-05 3 views
5

Sto lavorando con un dispositivo che restituisce un'immagine, e quando richiedo un'immagine, ci sono alcune informazioni non documentate che vengono prima dei dati dell'immagine. Sono stato in grado di realizzare questo solo guardando attraverso i dati binari e identificando le informazioni di intestazione dell'immagine all'interno.Problemi con i metodi di estensione per i matrici di byte

Originariamente avevo un metodo normale e l'ho convertito in un metodo di estensione. La domanda originale era relativa al compilatore che si lamentava di non avere Array come primo parametro (avevo Byte []), ma risulta che avevo commesso un errore e ho dimenticato di cancellare il primo argomento nel codice chiamante. In altre parole, ho usato per avere:

Byte[] new_buffer = RemoveUpToByteArray(buffer, new byte[] { 0x42, 0x4D }); 

e dopo aver cambiato ad un metodo di estensione, avevo erroneamente utilizzato:

buffer.RemoveUpToByteArray(buffer, new byte[] { 0x42, 0x4D }); 

In ogni caso, questo è tutto fisso ora perché ho realizzato il mio errore come ero inserendo l'esempio di codice in SO. Tuttavia,, ho un nuovo problema che è semplicemente la mancanza di comprensione dei metodi di estensione e di riferimento rispetto ai tipi di valore. Ecco il codice:

public static void RemoveFromByteArrayUntil(this Byte[] array, Byte[] until) 
{ 
    Debug.Assert(until.Count() > 0); 
    int num_header_bytes = until.Count(); 
    int header_start_pos = 0; // the position of the header bytes, defined by [until] 
    byte first_header_byte = until[0]; 
    while(header_start_pos != -1) { 
     header_start_pos = Array.IndexOf(array, first_header_byte, header_start_pos); 
     if(header_start_pos == -1) 
      break; 
     // if we get here, then we've found the first header byte, and we need to look 
     // for the next ones sequentially 
     for(int header_ctr=1; header_ctr<num_header_bytes; header_ctr++) { 
      // we're going to loop over each of the header bytes, but will 
      // bail out of this loop if there isn't a match 
      if(array[header_start_pos + header_ctr] != until[header_ctr]) { 
       // no match, so bail out. but before doing that, advance 
       // header_start_pos so the outer loop won't find the same 
       // occurrence of the first header byte over and over again 
       header_start_pos++; 
       break; 
      } 
     } 
     // if we get here, we've found the header! 
     // create a new byte array of the new size 
     int new_size = array.Count() - header_start_pos; 
     byte[] output_array = new byte[new_size]; 
     Array.Copy(array, header_start_pos, output_array, 0, new_size); 
     // here is my problem -- I want to change what array points to, but 
     // when this code returns, array goes back to its original value, which 
     // leads me to believe that the first argument is passed by value. 
     array = output_array; 
     return; 
    } 
    // if we get here, we didn't find a header, so throw an exception 
    throw new HeaderNotInByteArrayException(); 
} 

Il mio problema ora è che sembra che il primo questo argomento per il metodo di estensione è passato per valore. Voglio riassegnare a quale array punta, ma in questo caso sembra che dovrò semplicemente manipolare i dati dell'array.

+1

Non una risposta, solo un'osservazione: sembra che il tuo RemoveUpToByteArray debba creare un nuovo array di byte ogni volta che viene chiamato. Sarebbe più efficiente usare un intero come indice nell'array per tenere traccia di quanto lontano hai letto. –

+0

@Dave: non si è troppo specifici su dove si sta verificando l'errore del compilatore. Suppongo che sia al * call site * e non al metodo di estensione. Anche se lo è, non stai mostrando nessuno dei codici per nessuno dei due. È necessario approfondire questo aspetto per ottenere una risposta, poiché * è possibile * creare metodi di estensione su array di byte ed eseguirli facilmente. – casperOne

+0

ok, nessun problema! Vado avanti e postare il * codice * reale. :) Grazie per il feedback. – Dave

risposta

5

I metodi di estensione sono metodi statici che sembrano essere solo metodi di istanza. È possibile considerare l'istanza su cui sta lavorando il metodo di estensione da leggere solo (in base al valore). Assegnare al metodo di istanza di byte [] che è il primo parametro dell'estensione non funzionerà. Non sarà in grado di allontanarsi da assegnare, ma è possibile modificare l'estensione poi scrivere il tuo compito come questo:

buffer = buffer.RemoveUpToByteArray(header); 

Fai tornare il proprio interno il risultato array di byte, e non cercare di assegnare al buffer all'interno dell'estensione. La tua estensione sarebbe quindi simile a questa:

public static class MyExtensionMethods 
{ 
    public static byte[] RemoveUpToByteArray(this byte[] buffer, byte[] header) 
    { 
     byte[] result = buffer; 

     // your logic to remove header from result 

     return result; 
    } 
} 

Spero che questo aiuti.

MODIFICA: Quanto sopra è corretto solo per i tipi di valore. Se il tipo che si sta estendendo è un tipo di riferimento, non si avrebbe un problema operativo direttamente sul tipo come si sta tentando di fare sopra. Purtroppo, una matrice di byte è una struct, e quindi derivata da System.ValueType. Si consideri il seguente, che sarebbe perfettamente legale all'interno di un'estensione, e darebbe il risultato desiderato:

public class MyBytes 
{ 
    public byte[] ByteArray { get; set; } 
} 

public static class MyExtensionMethods 
{ 
    // Notice the void return here... 
    public static void MyClassExtension(this MyBytes buffer, byte[] header) 
    { 
     buffer.ByteArray = header; 
    } 
} 
+0

Ho un'altra soluzione che mi piace un po 'meglio ... La metterò nella mia domanda ora, non esitate a commentare. – Dave

+0

Lo riprendo, penso che tu abbia ragione, non ho altre opzioni. – Dave

+0

Qual è il punto di definire un metodo di estensione su una classe che hai già creato? Un metodo all'interno di "MYBytes" fa il lavoro. –

1

Mi dispiace che io non so quale sia il problema specifico che si è verificato, o il modo di risolverlo [certamente controllo si fa riferimento allo spazio dei nomi e la risoluzione di eventuali conflitti con metodi con nomi simili è un inizio], ma ho notato una o due stranezze.

Si consideri il seguente soluzione del campione,

using System.Linq; 
namespace Sample.Extensions 
{ 
    public static class ByteExtensions 
    { 
     public static void RemoveHeader (this byte[] buffer, byte[] header) 
     { 
      // take first sequence of bytes, compare to header, if header 
      // is present, return only content 
      // 
      // NOTE: Take, SequenceEqual, and Skip are standard Linq extensions 
      if (buffer.Take (header.Length).SequenceEqual (header)) 
      { 
       buffer = buffer.Skip (header.Length).ToArray(); 
      } 
     } 
    } 
} 

Questo compilato ed eseguito in VS2010RC.Per dimostrare l'utilizzo,

using Sample.Extensions; 
namespace Sample 
{ 
    class Program 
    { 
     static void Main (string[] args) 
     { 
      byte[] buffer = new byte[] { 00, 01, 02 }; 
      byte[] header = new byte[] { 00, 01 }; 
      buffer.RemoveHeader (header); 

      // hm, so everything compiles and runs, but buffer == { 00, 01, 02 } 
     } 
    } 
} 

Quindi non riceverà una compilazione o di runtime errore ma chiaramente non funzionerà come previsto. Questo perché le estensioni devono ancora rispettare la semantica del metodo standard, il che significa che i parametri vengono passati per valore. Non possiamo modificare buffer in modo che punti al nostro nuovo array.

possiamo risolvere questo problema riscrivendo il nostro metodo alla semantica delle funzioni convenzionali,

public static byte[] RemoveHeaderFunction (this byte[] buffer, byte[] header) 
{ 
    byte[] stripped = null; 
    if (stripped.Take (header.Length).SequenceEqual (header)) 
    { 
     stripped = stripped.Skip (header.Length).ToArray(); 
    } 
    else 
    { 
     stripped = buffer.ToArray(); 
    } 
    return stripped; 
} 

Ora

using Sample.Extensions; 
namespace Sample 
{ 
    class Program 
    { 
     static void Main (string[] args) 
     { 
      byte[] buffer = new byte[] { 00, 01, 02 }; 
      byte[] header = new byte[] { 00, 01 }; 

      // old way, buffer will still contain { 00, 01, 02 } 
      buffer.RemoveHeader (header); 

      // new way! as a function, we obtain new array of { 02 } 
      byte[] stripped = buffer.RemoveHeaderFunction (header); 
     } 
    } 
} 

Purtroppo, gli array sono i tipi di valore immutabili [possono fare uso di questi termini in modo non corretto]. L'unico modo per modificare il tuo "array" sul posto è di cambiare il contenitore in un tipo di riferimento mutabile, come un List<byte>.

Se siete veramente appassionati di "passando per ref", sul posto, effetto collaterale la semantica, quindi un'opzione possono essere le seguenti

using System.Linq; 
namespace Sample.Extensions 
{ 
    public static class ListExtensions 
    { 
     public static void RemoveHeader<T> (this List<T> list, List<T> header) 
     { 
      if (list.Take (header.Count).SequenceEqual (header)) 
      { 
       list.RemoveRange (0, header.Count); 
      } 
     } 
    } 
} 

Per quanto riguarda l'utilizzo,

static void Main (string[] args) 
{ 
    byte[] buffer = new byte[] { 00, 01, 02 }; 
    byte[] header = new byte[] { 00, 01 }; 

    List<byte> bufferList = buffer.ToList(); 

    // in-place side-effect header removal 
    bufferList.RemoveHeader (header.ToList()); 
} 

Sotto il cofano, List<T> sta mantenendo un array di tipo T. A certe soglie, è semplicemente manipolazione dell'array sottostante e \ o istanziazione di nuovi array per noi.

Spero che questo aiuti! :)

+0

Grazie, Johnny. Audie arrivò alla stessa conclusione ... Vorrei che fosse possibile passare il primo parametro come riferimento. Ad esempio, perché non posso usare "ref" con "this"? – Dave

Problemi correlati