2010-07-11 3 views
10

Ho più oggetti di grandi dimensioni ciascuno con circa 60 stringhe. Devo tagliare tutte quelle stringhe, e mi piacerebbe farlo senza dover andare this.mystring = this.mystring.Trim(). Invece, sto cercando un modo per avere automaticamente ogni oggetto scoprire le proprie stringhe e quindi eseguire l'operazione.Iterate tramite le proprie stringhe e ritagli dell'oggetto ciascuna

Conosco un po 'di riflessione, ma non abbastanza, ma penso che sia possibile?

Inoltre, non sono sicuro se ciò è importante, ma alcune proprietà di stringa sono di sola lettura (solo un getter), quindi tali proprietà dovrebbero essere saltate.

Aiuto?

risposta

14

Bene, è abbastanza facile ottenere tutte le proprietà e scoprire quali sono stringhe e scrivibili. LINQ rende ancora più semplice.

var props = instance.GetType() 
        .GetProperties(BindingFlags.Instance | BindingFlags.Public) 
        // Ignore non-string properties 
        .Where(prop => prop.PropertyType == typeof(string)) 
        // Ignore indexers 
        .Where(prop => prop.GetIndexParameters().Length == 0) 
        // Must be both readable and writable 
        .Where(prop => prop.CanWrite && prop.CanRead); 

foreach (PropertyInfo prop in props) 
{ 
    string value = (string) prop.GetValue(instance, null); 
    if (value != null) 
    { 
     value = value.Trim(); 
     prop.SetValue(instance, value, null); 
    } 
} 

Si consiglia di impostare solo la proprietà se rifilatura in realtà fa la differenza, per evitare calcoli ridondanti oggetti complessi - o potrebbe non essere un problema per voi.

Ci sono vari modi per migliorare le prestazioni, se necessario - le cose come:

  • sufficiente la memorizzazione nella cache le proprietà rilevanti per ogni tipo di
  • Utilizzando Delegate.CreateDelegate per costruire delegati per i getter e setter
  • Possibilmente usando alberi di espressione, anche se non sono sicuro che possano essere d'aiuto qui

Non prenderei nessuna di quelle e in realtà è un problema.

+0

Ho pensato che gli alberi delle proprietà vengano automaticamente memorizzati nella cache se non in modalità di debug? L'ho letto alcune volte. Non è vero? – Alex

+1

In questo caso, 'Expression' sarebbe un dolore, anche in 4.0; combinare più 'Azione ' in un singolo delegato funzionerebbe bene, però. –

+0

@Alex - le espressioni lambda nel * source * possono fare cose intelligenti, usando un campo di delegati di supporto. Tuttavia, AFAIK non è vero per gli alberi costruiti manualmente e compilati ('Compile') in fase di runtime. –

3

Qualcosa di simile:

foreach (PropertyInfo prop in obj.GetType().GetProperties(
     BindingFlags.Instance | BindingFlags.Public)) 
    { 
     if (prop.CanRead && prop.CanWrite && prop.PropertyType == typeof(string) 
      && (prop.GetIndexParameters().Length == 0)) // watch for indexers! 
     { 
      var s = (string)prop.GetValue(obj, null); 
      if (!string.IsNullOrEmpty(s)) s = s.Trim(); 
      prop.SetValue(obj, s, null); 
     } 
    } 
+0

Il parametro 'Tipo.La chiamata a GetProperties() include proprietà statiche, che io * sospetto * non dovrebbe essere inclusa qui. –

+1

@Jon - buon punto, grazie. Non dirò "IsReadable"; p Inoltre: potresti voler controllare gli indicizzatori. –

+0

@Marc: risolverà :) –

0

Quindi, per espandere su questo un po ', ho un oggetto complesso con liste di liste e volevo attraversare che e tagliare tutta la stringa di bambino oggetti pure. Sto postando ciò che ho fatto su ciò che ho creato da @Jon nella sua risposta. Sono curioso di sapere se c'era un modo migliore per farlo o se mi mancava qualcosa di ovvio.

Gli oggetti che ho sono più complessi di questo ma dovrebbero illustrare ciò che stavo provando.

public class Customer 
{ 
    public string Name { get; set; } 
    public List<Contact> Contacts { get; set; } 
} 

public class Contact 
{ 
    public string Name { get; set; } 
    public List<Email> EmailAddresses {get; set;} 
} 

public class Email 
{ 
    public string EmailAddress {get; set;} 
} 


    private void TrimWhitespace(object instance) 
    { 
     if (instance != null) 
     { 
      var props = instance.GetType() 
        .GetProperties(BindingFlags.Instance | BindingFlags.Public) 
       // Ignore indexers 
        .Where(prop => prop.GetIndexParameters().Length == 0) 
       // Must be both readable and writable 
        .Where(prop => prop.CanWrite && prop.CanRead); 

      foreach (PropertyInfo prop in props) 
      { 
       if (instance is IEnumerable) 
       { 
        foreach (var item in (IEnumerable)instance) 
        { 
         TrimWhitespace(item); 
        } 
       } 
       else if (prop.GetValue(instance, null) is string) 
       { 
        string value = (string)prop.GetValue(instance, null); 
        if (value != null) 
        { 
         value = value.Trim(); 
         prop.SetValue(instance, value, null); 
        } 
       } 
       else 
        TrimWhitespace(prop.GetValue(instance, null)); 
      } 
     } 
    } 

Pensieri?

1

Non è necessario effettuare il controllo IEnumerable nel loop di oggetti e se l'istanza attuale è un IEnumerable, gli oggetti di scena vengono ignorati. Correzione per IEnumerable parte:

private void TrimWhitespace(object instance) 
{ 
    if (instance != null) 
    { 
     if (instance is IEnumerable) 
     { 
      foreach (var item in (IEnumerable)instance) 
      { 
       TrimWhitespace(item); 
      } 
     } 

     var props = instance.GetType() 
       .GetProperties(BindingFlags.Instance | BindingFlags.Public) 
      // Ignore indexers 
       .Where(prop => prop.GetIndexParameters().Length == 0) 
      // Must be both readable and writable 
       .Where(prop => prop.CanWrite && prop.CanRead); 

     foreach (PropertyInfo prop in props) 
     { 
      if (prop.GetValue(instance, null) is string) 
      { 
       string value = (string)prop.GetValue(instance, null); 
       if (value != null) 
       { 
        value = value.Trim(); 
        prop.SetValue(instance, value, null); 
       } 
      } 
      else 
       TrimWhitespace(prop.GetValue(instance, null)); 
     } 
    } 
}