将文本添加到列表的所有项目中的所有属性,而无需手动多次循环

时间:2018-11-24 17:22:24

标签: c# linq

我有一个MyClass列表

public class MyClass
{
    public string Title { get; set; }
    public string Name { get; set; }
}


var records = new List<MyClass>
{
    new MyClass { Title = "Mr", Name = "Bob" },
    new MyClass { Title = "Dr", Name = "Jon" },
};

我需要在类的每个属性中的每个值以及列表中的每个项目周围添加“”。因此,每个属性的实际内容在开头和结尾处都包含一个“。

有没有一种方法可以添加此属性,而不必遍历每个属性和项目并手动完成。也许是使用Linq的方法?

我在Google上找不到任何东西,我甚至不确定要搜索什么。

谢谢

2 个答案:

答案 0 :(得分:3)

是的,只需映射转换即可:

var transformedRecords =  
    records.Select(c => 
        new MyClass 
        { 
            Title = $"\"{c.Title}\"",
            Name = $"\"{c.Name}\"" 
        });

现在,如果您想要的是一种无需手动手动更改所有string属性的方法,那么唯一的方法就是反射:

static void Transform<T>(T t)
    where T: class
{
    if (ReferenceEquals(t, null))
        throw new ArgumentNullException(nameof(t));

    var propertiesToEdit =
        typeof(T).GetProperties(BindingFlags.Public |
                                BindingFlags.Instance)
                 .Where(p => p.PropertyType == typeof(string)
                             p.CanWrite &&
                             p.CanRead);

    foreach (var p in propertiesToEdit)
    {
        p.SetValue(t, $"\"{p.GetValue(t)}\"");
    }
}

但是现在您有了一个方法,可以对传递给它的对象进行突变。永远不要在LINQ上使用变异方法,否则会出现意想不到的行为。

因此,最好只是简单地手动进行迭代:

foreach (var r in records)
    Transform(r);

现在您的records列表将包含突变的项目。

当然,您无需在string停留,可以用很少的额外费用使这一点变得更加普遍:

static void Transform<T, Q>(T t, Func<Q, Q> transform)
    where T: class
{
    if (ReferenceEquals(t, null))
       throw new ArgumentNullException(nameof(t));

    var propertiesToEdit = 
        typeof(T).GetProperties(BindingFlags.Public |
                                BindingFlags.Instance)
                 .Where(p => p.PropertyType == typeof(Q)
                             p.CanWrite &&
                             p.CanRead);

    foreach (var p in propertiesToEdit)
    {
        p.SetValue(t, transform((Q)p.GetValue(t)));
    }
}

现在您将像这样使用它:

foreach (var r in records)
    Transform(r, (string s) => $"\"{s}\"");

class约束在这里很重要,因为这会因值类型而失败; c#默认使用按值传递语义,因此该方法将变异副本,而不是列表中的原始实例。

答案 1 :(得分:0)

在不使用循环的情况下,我相信这是您想要的:

 records.ForEach(e => 
        {
            e.Name = $"\"{e.Name}\"";
            e.Title = $"\"{e.Title}\"";
        });