使用C#中的List,反射修剪列表中的所有字符串

3

我有一个方法可以修剪第一层中的所有字符串。

public static IEnumerable<T> Trim<T>(this IEnumerable<T> collection)
{
    foreach (var item in collection)
    {
        var stringProperties = item.GetType().GetProperties()
                  .Where(p => p.PropertyType == typeof(string));

        foreach (var stringProperty in stringProperties)
        {
            var currentValue = (string)stringProperty.GetValue(item, null);

            if (currentValue != null)
                stringProperty.SetValue(item, currentValue.Trim(), null);
        }
    }
    return collection;
}

但如果我的属性是一个列表,我需要在该列表中的所有字符串属性中应用 trim 功能,有人可以帮助我吗?


7
为什么不使用递归? - Zohar Peled
1
@StephenBrickner那样做行不通,因为string是不可变的。 - juharr
你真的需要通过在 foreach 结尾处使用 yield return item 来使你的方法变为惰性方法,或者将其更改为接受类似于 ICollection<T> 的东西。当前状态下,当调用时它将迭代 IEnumerable<T> 并返回相同的 IEnumerable<T>,如果底层类型是像 List<T> 这样的东西,那么这是可以的,但它可能是一个每次迭代都会生成其项的序列,这意味着你将失去所有更改。 - juharr
1
如果属性的类型是 List<List<List<List<string>>>>,应该使用多少层深度? - Fabjan
2
但如果我的属性是一个列表,那么这个列表包含什么?字符串?其他对象?结构体呢?为什么你需要暗中修剪所有的字符串呢? - D Stanley
显示剩余2条评论
2个回答

4
public static IEnumerable<T> Trim<T>(this IEnumerable<T> collection)
    where T:class 
{
    foreach (var item in collection)
    {
        var properties = item.GetType().GetProperties();

        // Loop over properts
        foreach (var property in properties)
        {
            if (property.PropertyType == typeof (string))
            {
                var currentValue = (string)property.GetValue(item);
                if (currentValue != null)
                    property.SetValue(item, currentValue.Trim());
            }
            else if (typeof(IEnumerable<object>).IsAssignableFrom(property.PropertyType))
            {
                var currentValue = (IEnumerable<object>)property.GetValue(item);
                if (currentValue != null)
                    currentValue.Trim();
            }
        }
    }
    return collection;
}

编辑:包括 yield

编辑2:再次删除 yield。我知道这对 IEnumerable 扩展来说是不好的做法。然而,另一种选择是:

            else if (typeof(IEnumerable<object>).IsAssignableFrom(property.PropertyType))
            {
                var currentValue = (IEnumerable<object>)property.GetValue(item);
                if (currentValue != null)
                    currentValue.Trim().ToList(); // Hack to enumerate it!
            }
        }
    }
    yield return item;
}

如果我没记错的话,它应该是一个递归函数。当访问一个集合时,我会对新集合调用Trim(),然后重新开始。 - Toxantron
是的,我看错了,但你确实需要设置嵌套的 IEnumerable,以确保懒加载实现生效。 - juharr
1
这样是行不通的。由于协变性,我可以将其转换为 IEnumerable<object>,但我无法将其设置为类型为 List<Foo> 的属性。不过你是对的。现在它什么也没做。 - Toxantron
在这种情况下,var currentValue = (IEnumerable<object>)property.GetValue(item);是同一个对象,因此您无法调用Trim()方法。 - Glauco Cucchiar
为什么是同一个对象?(IEnumerable<object>)property.GetValue(item)从实例item中读取属性值。因此它不是同一个对象,或者我错了吗? - Toxantron
没关系,我觉得命名有点混乱。 ;) - Toxantron

0

假设您需要就地修改源集合中的字符串(如您的示例所示),那么我认为最简单的方法可能是添加一个额外的扩展方法,专门处理字符串列表,然后利用您示例中的扩展方法进行修改,以便特定于字符串的IEnumerables。新的扩展方法将非常简单,如下所示:

public static IEnumerable<List<string>> Trim( this IEnumerable<List<string>> collection )
{
    foreach(var item in collection)
    {
        item.Trim();
    }
    return collection;
}

然后您的扩展将被调整如下:

public static IEnumerable<string> Trim( this IEnumerable<string> collection )
{
    foreach( var item in collection )
    {
        var stringProperties = item.GetType().GetProperties()
                    .Where( p => p.PropertyType == typeof( string ) );

        foreach( var stringProperty in stringProperties )
        {
            var currentValue = (string)stringProperty.GetValue( item, null );

            if( currentValue != null )
                stringProperty.SetValue( item, currentValue.Trim(), null );
        }
    }
    return collection;
}

我保留了您返回原始集合的模式,我认为这是为了提供您所需的某种类型的方法链接。

当然,这并不处理递归,但从我所看到的来看,这里并不需要。


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