迭代对象自身的字符串,并修剪每个字符串

12

我有多个大型对象,每个对象都有大约60个字符串。 我必须修剪所有这些字符串,并且我希望能够自动地使每个对象发现其自己的字符串并执行操作,而不必使用“this.mystring = this.mystring.Trim()”。

我知道一些关于反射的知识,但还不够,但我认为这是可能的?

另外,我不确定是否重要,但是一些字符串属性是只读的(只有getter),因此这些属性必须被跳过。

请帮忙?

4个回答

17

获取所有属性并找出其中字符串类型和可写的属性很容易。使用LINQ可以更加简单。

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

如果修剪实际上没有任何区别,您可能只想在需要时设置属性,以避免对于复杂的属性进行冗余计算-或者这可能对您没有影响。

如果有必要,有各种改善性能的方法,例如:

  • 为每种类型简单地缓存相关属性
  • 使用Delegate.CreateDelegate来构建获取器和设置器的委托
  • 可能使用表达式树,尽管我不确定它们是否有帮助

除非性能真正成为问题,否则不建议采取任何这些措施。


我认为如果不是在调试模式下,属性树会自动被缓存,我读过几次。这不是真的吗? - Alex
1
在这种情况下,即使在4.0中,Expression也会很麻烦;将多个Action<T,string>组合成单个委托会很好地解决问题。 - Marc Gravell
@Alex - 在源代码中,lambda表达式可以通过使用支持委托字段来完成一些巧妙的事情。然而,据我所知,手动构建并在运行时编译(Compile)的树不具备这种能力。 - Marc Gravell
你的示例缺少从prop.GetValue到String的转换。 - Alex

3

Something like:

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

不带参数的 Type.GetProperties() 调用包括静态属性,我怀疑这里不应该包括它们。 - Jon Skeet
1
@Jon - 很好的观点,谢谢。我不会提到IsReadable ;p 另外 - 你可能想检查索引器。 - Marc Gravell
你能解释一下为什么要检查prop.GetIndexParameters().Length == 0吗?这是做什么用的? - Alex
@Alex:它可以防止它捕捉到索引器。 - Jon Skeet
@Alex - 索引器类似于 public string this[int i] {get {...} set {...}},它作为一种特殊类型的属性出现。 - Marc Gravell

1

在 props 循环中不需要检查 IEnumerable,如果实际实例是 IEnumerable,则忽略 props。针对 IEnumerable 的修复:

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

0

稍微扩展一下,我有一个包含列表的复杂对象,我想遍历它并修剪所有子字符串对象。这是我根据@Jon答案所构建的代码。如果有更好的方法或者我错过了什么明显的东西,我很好奇。

我的对象比这个更复杂,但这应该说明我在尝试什么。

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

有何想法?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接