2009-05-05 13 views
46

Estoy tratando de hacer un método que vaya a través de una lista de objetos genéricos y reemplazar todas sus propiedades del tipo string que es o vacío con un reemplazo.C#: Cómo obtener todas las propiedades de cadena públicas (tanto obtener y establecer) de un tipo

¿Cómo es una buena manera de hacer esto?

tengo este tipo de concha ... ... hasta ahora:

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); 
     } 
    } 
} 

Así que, ¿cómo puedo encontrar todas las propiedades de un tipo que son:

  • De tipo string
  • Ha pública get
  • Ha pública set

    ?


Hice esta clase de prueba:

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 siguiente no funciona:

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

Si imprimo el nombre de aquellas propiedades que consigo allí, consiga:

PublicPublic público- PrivatePublic

En otras palabras, me sale dos propiedades demasiado.


Nota: Esto probablemente se podría hacer de una manera mejor ... usando foreach y reflexión anidada y todos aquí ... pero si tiene alguna grandes ideas alternativas, por favor hágamelo saber porque yo ¡querer aprender!

Respuesta

79

Su código se reescribió. No usa LINQ ni 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

En primer lugar, su ejemplo reemplazará los valores * todo * y, en segundo lugar, la propiedad CanWrite no parece funcionar de la manera que pensamos que sería ... =/ – Svish

+0

El CanWrite funciona de la manera que debería. ¿Puedes explicar qué te hace pensar lo contrario? –

+0

Como muestra mi ejemplo, una propiedad pública que se declara como i.e.cadena pública Something {get; private set;}, devolverá true para CanRead y CanWrite, aunque no debería poder escribir, ya que el setter es privado. – Svish

0

BindingFlags.Public | BindingFlags.Instance debe hacerlo

GetSetMethod()

9

Encontrará las propiedades tales como la BindingFlags.Public | BindingFlags.Instance. A continuación, deberá examinar cada instancia de PropertyInfo marcando las propiedades CanWrite y CanRead para ver si son legibles o escribibles.

Actualización: código de ejemplo

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 
    } 
} 

Miré en ella con más detalle después de su actualización.Si también examina los objetos MethodInfo devueltos por GetGetMethod y GetSetMethod, llegará al objetivo, creo;

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

Por defecto estos dos métodos de vuelta Solamente captadores y definidores públicas (que arriesgan una NullReferenceException en un caso como éste), pero al pasar por encima de true como los hace también vuelven las privadas. Luego puede examinar las propiedades IsPublic (o IsPrivate).

+0

Esto no funciona. Ver mi ejemplo. Aunque el get o el set es privado, dice que puede leer y escribir, siempre que uno de ellos sea público. – Svish

0

Sugiero un enfoque diferente: AOP.
Puede interceptar el colocador y establecer el valor deseado en uno válido. Con PostSharp es bastante fácil.

+0

¿Eh? ¿Cómo? – Svish

+0

Como estaba diciendo, es un enfoque diferente. En lugar de cambiar el valor * después de que esté configurado, sugiero interceptar el colocador y cambiar el valor si es necesario. Al utilizar PostSharp, puede escribir atributos y usarlos con sus propiedades. –

+0

¿cómo interceptarías a un colocador? ¿No necesitarías tener acceso a la clase o anular las propiedades o algo así? – Svish

1

Si no especifica ninguna marca de encuadernación, obtendrá las propiedades de instancia públicas, que es lo que desea. Pero luego deberá verificar si PropertyType en el objeto PropertyInfo es de tipo String. A menos que sepa con anticipación, también deberá verificar si la propiedad es legible/escribible como lo indica @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

Estoy de acuerdo con otras respuestas, pero yo prefiero que refactorizar la búsqueda en sí misma a ser easly consulta con LINQ, por lo que la consulta podría ser el siguiente:

 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)); 

Tomé de mi ejemplo la Asamblea tipo, que no ha de lectura/escritura propiedades de cadena, pero si la misma búsqueda de código para simplemente leer las propiedades, el resultado será:

  • CodeBase
  • Esc apedCodeBase
  • NombreCompleto
  • Ubicación
  • ImageRuntimeVersion

¿Cuáles son la cadena de sólo lectura propiedades de tipo de montaje

Cuestiones relacionadas