这是操纵子类型集合的唯一 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;
}
在一个项目中,我有一组 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;
}