2009-05-05 11 views
46

Sto provando a creare un metodo che passerà attraverso un elenco di oggetti generici e sostituiremo tutte le loro proprietà di tipo string che è o null o vuoto con una sostituzione.C#: Come ottenere tutte le proprietà di stringa pubbliche (sia get che set) di un tipo

Come è un buon modo per farlo?

ho questo tipo di ... shell ... finora:

public static void ReplaceEmptyStrings<T>(List<T> list, string replacement) 
{ 
    var properties = typeof(T).GetProperties(-- What BindingFlags? --); 

    foreach(var p in properties) 
    { 
     foreach(var item in list) 
     { 
      if(string.IsNullOrEmpty((string) p.GetValue(item, null))) 
       p.SetValue(item, replacement, null); 
     } 
    } 
} 

Allora, come faccio a trovare tutte le proprietà di un tipo che sono:

  • Di tipo string
  • Ha pubblico get
  • Ha pubblico set

    ?


ho fatto questa classe di test:

class TestSubject 
{ 
    public string Public; 
    private string Private; 

    public string PublicPublic { get; set; } 
    public string PublicPrivate { get; private set; } 
    public string PrivatePublic { private get; set; } 
    private string PrivatePrivate { get; set; } 
} 

La seguente non funziona:

var properties = typeof(TestSubject) 
     .GetProperties(BindingFlags.Instance|BindingFlags.Public) 
     .Where(ø => ø.CanRead && ø.CanWrite) 
     .Where(ø => ø.PropertyType == typeof(string)); 

se stampo il nome di queste proprietà ci si arriva, mi get:

PublicPublic pubblico e privato PrivatePublic

In altre parole, ottengo due proprietà troppo.


Nota: Questo potrebbe essere fatto probabilmente in un modo migliore ... utilizzando foreach nidificato e riflessione e tutti qui ... ma se avete grandi idee alternative, per favore fatemelo sapere perche 'io voglio imparare!

risposta

79

Il tuo codice è stato riscritto. Non usa LINQ né var.

public static void ReplaceEmptyStrings<T>(List<T> list, string replacement) 
{ 
    PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); 

    foreach (PropertyInfo p in properties) 
    { 
     // Only work with strings 
     if (p.PropertyType != typeof(string)) { continue; } 

     // If not writable then cannot null it; if not readable then cannot check it's value 
     if (!p.CanWrite || !p.CanRead) { continue; } 

     MethodInfo mget = p.GetGetMethod(false); 
     MethodInfo mset = p.GetSetMethod(false); 

     // Get and set methods have to be public 
     if (mget == null) { continue; } 
     if (mset == null) { continue; } 

     foreach (T item in list) 
     { 
      if (string.IsNullOrEmpty((string)p.GetValue(item, null))) 
      { 
       p.SetValue(item, replacement, null); 
      } 
     } 
    } 
} 
+0

L'esempio innanzitutto sostituirà * tutti i valori * e, in secondo luogo, la proprietà CanWrite non sembra funzionare come penseremmo sarebbe ... =/ – Svish

+0

CanWrite funziona davvero come dovrebbe. Puoi spiegare cosa ti fa pensare al contrario? –

+0

Come mostra il mio esempio, una proprietà pubblica dichiarata come i.e.stringa pubblica Something {get; set privato;}, restituisce true sia per CanRead sia per CanWrite, anche se non dovrei riuscire a scrivere, poiché il setter è privato. – Svish

0

BindingFlags.Public | BindingFlags.Instance dovrebbe farlo

GetSetMethod()

9

Troverete le proprietà in quanto tali con BindingFlags.Public | BindingFlags.Instance. Quindi sarà necessario esaminare ciascuna istanza di PropertyInfo controllando le proprietà CanWrite e CanRead, al fine di scoprire se sono leggibili e/o scrivibili.

Aggiornamento: codice di esempio

PropertyInfo[] props = yourClassInstance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); 
for (int i = 0; i < props.Length; i++) 
{ 
    if (props[i].PropertyType == typeof(string) && props[i].CanWrite) 
    { 
     // do your update 
    } 
} 

ho guardato in più in dettaglio dopo l'aggiornamento.Se si esaminano anche gli oggetti MethodInfo restituiti da GetGetMethod e GetSetMethod, si raggiungerà l'obiettivo, penso;

var properties = typeof(TestSubject).GetProperties(BindingFlags.Instance | BindingFlags.Public) 
     .Where(ø => ø.CanRead && ø.CanWrite) 
     .Where(ø => ø.PropertyType == typeof(string)) 
     .Where(ø => ø.GetGetMethod(true).IsPublic) 
     .Where(ø => ø.GetSetMethod(true).IsPublic); 

Per impostazione predefinita questi due metodi restituire solo getter e setter pubblici (rischiando un NullReferenceException in un caso come questo), ma passando true come sopra li fa tornare anche quelle private. Quindi è possibile esaminare le proprietà IsPublic (o IsPrivate).

+0

Questo non funziona. Guarda il mio esempio Anche se get o set è privato, dice che può sia leggere che scrivere, purché uno di essi sia pubblico. – Svish

0

Suggerisco un approccio diverso: AOP.
È possibile intercettare il setter e impostare il valore desiderato su un valore valido. Con PostSharp è abbastanza facile.

+0

Eh? Come? – Svish

+0

Come stavo dicendo, è un approccio diverso. Invece di modificare il valore * dopo che * è impostato, suggerisco di intercettare il setter e modificare il valore se necessario. Quando si utilizza PostSharp, è possibile scrivere attributi e utilizzarli con le proprietà. –

+0

come intercettate un setter? non avresti bisogno di accedere alla classe o sostituire le proprietà o qualcosa del genere? – Svish

1

Se non si specifica alcun flag di binding, si otterrà il pubblico, proprietà di istanza, che è ciò che si desidera. Ma allora sarà necessario verificare se il PropertyType sull'oggetto PropertyInfo è di tipo String. A meno che tu non sappia in anticipo, dovrai anche verificare se la proprietà è leggibile/scrivibile come indicato da @Fredrik.

using System.Linq; 

public static void ReplaceEmptyStrings<T>(List<T> list, string replacement) 
{ 
    var properties = typeof(T).GetProperties() 
           .Where(p => p.PropertyType == typeof(string)); 
    foreach(var p in properties) 
    { 
     foreach(var item in list) 
     { 
      if(string.IsNullOrEmpty((string) p.GetValue(item, null))) 
       p.SetValue(item, replacement, null); 
     } 
    } 
} 
0

sono d'accordo con le altre risposte, ma preferisco di refactoring la ricerca in sé ad essere facilmente interrogato con LINQ, quindi la query potrebbe essere la seguente:

 var asm = Assembly.GetExecutingAssembly(); 
     var properties = (from prop 
           in asm.GetType() 
           .GetProperties(BindingFlags.Public | BindingFlags.Instance) 
          where 
          prop.PropertyType == typeof (string) && 
          prop.CanWrite && 
          prop.CanRead 
          select prop).ToList(); 
     properties.ForEach(p => Debug.WriteLine(p.Name)); 

ho preso per il mio esempio l'Assemblea tipo, che non è di lettura/scrittura proprietà di stringa, ma se la stessa ricerca di codice solo per leggere le proprietà, il risultato sarà:

  • Codebase
  • Esc apedCodeBase
  • FullName
  • Località
  • ImageRuntimeVersion

Quali sono la stringa sola lettura montaggio proprietà del tipo

Problemi correlati