这是操纵子类型集合的唯一 DRY 方法吗?

Is this the only DRY way to manipulate subtype collections?

在一个项目中,我有一组 classes 实现了一个抽象基础 class...

public abstract class BaseClass
{
    Guid Id { get; set; }
    string Name { get; set; }
    string Property { get; set; }
}

public class Foo : BaseClass

public class Bar : BaseClass

/*etc*/

...对于另一个 class,我需要在集合中对它们执行一些操作。唯一需要操纵的是基地 class 的成员。由于集合的原因,为每种类型编写一个方法有点重复,我觉得我错过了一些东西。这是我目前所拥有的:

public ICollection<BaseClass> Transform(ICollection<BaseClass> source, Guid newId, Func<BaseClass> factory)
{
    ICollection<BaseClass> result = new List<BaseClass>();

    foreach (BaseClass x in source)
    {
        BaseClass record = factory();

        record.Id = newId;
        record.Name = x.Name;
        record.Property = x.Property;

        result.Add(record);
    }

    return result;
}

public ICollection<Foo> TransformFoos(ICollection<Foo> source, string property)
{
    Func<BaseClass> factory = () => new Foo();
    ICollection<BaseClass> sourceAsBase = source.Select(x => (BaseClass)x).ToList();
    ICollection<Foo> result = this.Transform(sourceAsBase, property, factory)
                                  .Select(x => (Foo)x)
                                  .ToList();
    return result;
}

public ICollection<Bar> TransformBars(ICollection<Bar> source, string property)

/*etc*/

不过,这是一个很多的转变。有没有更好的方法?

几乎任何时候你看到代码做的事情完全相同但类型不同,这意味着你应该使用泛型。在这里,您可以简单地使您的转换方法通用,将其限制为基本 class 类型,然后一切就绪。

public List<T> Transform<T>(IEnumerable<BaseClass> source, string property)
    where T : BaseClass, new()
{
    return source.Select(sourceItem => new T()
    {
        Id = sourceItem.Id,
        Name = sourceItem.Name,
        Property = property,
    }).ToList();
}

如果您想更新在子类型上定义的属性,您可以使用模式匹配新的 C# 功能,而不是为每个子类型编写单独的方法。

    switch(x)
    {
        case Foo f:
            f.Foo1 = "Updated foo";
        break;
        case Bar b:
            b.Bar1 = "Updated bar";
        break;
    }

下面是你用同样的更新方法。

public static ICollection<BaseClass> Transform(ICollection<BaseClass> source, string property, Func<BaseClass> factory)
{
    ICollection<BaseClass> result = new List<BaseClass>();

    foreach (BaseClass x in source)
    {
        BaseClass record = factory();

        record.Id = x.Id;
        record.Name = x.Name;
        record.Property = property;

        result.Add(record);

        switch(x)
        {
            case Foo f:
                f.Foo1 = "Updated foo";
            break;
            case Bar b:
                b.Bar1 = "Updated bar";
            break;
        }
    }

    return result;
}