C #: Как получить все открытые (как получить, так и установить) строковые свойства типа - PullRequest
54 голосов
/ 05 мая 2009

Я пытаюсь создать метод, который будет проходить через список универсальных объектов и заменять все их свойства типа string, который либо null, либо пуст, заменой.

Как хороший способ сделать это?

У меня есть такая ... оболочка ... пока:

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

Итак, как мне найти все свойства типа:

  • типа string
  • Имеет публичную get
  • Имеет публичную set


Я сделал этот тестовый класс:

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

Не работает следующее:

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

Если я распечатаю Имя тех свойств, которые я там получаю, я получаю:

PublicPublic государственно-частное PrivatePublic

Другими словами, я получаю два свойства слишком много.


Примечание : Вероятно, это можно было бы сделать лучше ... используя вложенный foreach и рефлексию и все здесь ... но если у вас есть отличные альтернативные идеи, пожалуйста, дайте мне знать потому что я хочу учиться!

Ответы [ 7 ]

88 голосов
/ 05 мая 2009

Ваш код переписан. Не использует ни LINQ, ни 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);
            }
        }
    }
}
9 голосов
/ 05 мая 2009

Вы найдете свойства как таковые с BindingFlags.Public | BindingFlags.Instance. Затем вам нужно будет проверить каждый экземпляр PropertyInfo, проверив свойства CanWrite и CanRead, чтобы выяснить, доступны ли они для чтения и / или записи.

Обновление: пример кода

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

Я рассмотрел это более подробно после вашего обновления. Если вы также изучите объекты MethodInfo, возвращаемые GetGetMethod и GetSetMethod, вы попадете в цель, я думаю;

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

По умолчанию эти два метода возвращают только общедоступные методы получения и установки (рискуя исключением NullReferenceException в подобном случае), но передача true, как указано выше, заставляет их также возвращать закрытые. Затем вы можете проверить свойства IsPublic (или IsPrivate).

1 голос
/ 03 мая 2010

http://jefferytay.wordpress.com/2010/05/03/simple-and-useful-tostring/

для метода переопределения tostring, который позволяет вам получить все свойства класса

1 голос
/ 05 мая 2009

Если вы не укажете какие-либо флаги привязки, вы получите общедоступные свойства экземпляра - это то, что вам нужно. Но тогда вам нужно будет проверить, имеет ли PropertyType объекта PropertyInfo тип String. Если вы не знаете заранее, вам также необходимо проверить, является ли свойство доступным для чтения / записи, как указывает @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 голосов
/ 05 мая 2009

Я согласен с другими ответами, но я предпочитаю рефакторинг самого поиска, чтобы его можно было легко запросить с помощью Linq, поэтому запрос может быть следующим:

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

Я взял в качестве примера тип Assembly, который не имеет свойств строки для чтения / записи, но если тот же код выполняет поиск только для свойств чтения, результат будет:

  • CodeBase
  • EscapedCodeBase
  • ПолноеИмя
  • Местоположение
  • ImageRuntimeVersion

Какие строки только для чтения Свойства типа сборки

0 голосов
/ 05 мая 2009

Я предлагаю другой подход: AOP .
Вы можете перехватить сеттер и установить желаемое значение на правильное. С PostSharp это довольно просто.

0 голосов
/ 05 мая 2009

BindingFlags.Public | BindingFlags.Instance должен сделать это

GetSetMethod ()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...