我有下面的代码,它在我的代码中的两个位置实现了我需要的逻辑。代码以通用方式编写。
它有缺陷。它不检查给定对象是否已存在于collectionToBeExtended中。它只是添加它,我将有两倍相同的项目。
我使用此代码管理两个集合,一个包含标签,另一个包含链接实体(POCO)。它们没有相同的父对象,也没有接口。为了管理下面的情况,我不想为他们引入新的通用界面。
为了修复上面的缺陷,我需要在foreach中放置自定义逻辑,以检查给定对象是否已经是集合的成员。 Tag和Entity对象是不同的,因此有比较的共同点。您可以在下面看到Tag实现。
DRY原则显然我不应该创建确切的实现,而是我应该创建一些通用解决方案来处理这两种情况,或者第三种和第四种情况。我的第一个想法是,我引入了一个开关,我可以根据对象类型放置自定义逻辑,但Martin Fowler在他的重构书中说切换案例是臭区。
我目前的知识和经验不足以解决这个问题,所以我真的很感激任何帮助!
通用实施:
private ICollection<T> AddTo<T> ( IEnumerable<T> toBeAdded , ICollection<T> collectionToBeExtended )
{
if ( collectionToBeExtended == null )
{
collectionToBeExtended = new List<T> ( );
}
if ( toBeAdded != null )
{
foreach ( T obj in toBeAdded )
{
collectionToBeExtended.Add(obj);
}
}
return collectionToBeExtended;
}
代码实施:
private ICollection<Tag> AddTo ( IEnumerable<Tag> toBeAdded , ICollection<Tag> collectionToBeExtended )
{
if ( collectionToBeExtended == null )
{
collectionToBeExtended = new List<Tag> ( );
}
if ( toBeAdded != null )
{
foreach ( Tag tag in toBeAdded )
{
if (!collectionToBeExtended.Any(c => c.Id == tag.Id && c.Name == tag.Name))
{
collectionToBeExtended.Add(tag);
}
}
}
return collectionToBeExtended;
}
答案 0 :(得分:3)
C#中的所有类型都可以等同于x.Equals(y)
。如果您愿意,可以覆盖此等式。
我只想用IEnumerable<A>
来解决这个问题。如果您之后想要ICollection<A>
,那么您可以从IEnumerable<A>
构建一个集合。
collectionToBeExtended.Concat(toBeAdded).Distinct()
答案 1 :(得分:2)
我希望这不是矫枉过正,但扩展方法似乎对我有用。我希望它对你也有所帮助。
public static ICollection<X> AddIf<X>(this ICollection<X> myarray, X val, Func<X, X, bool> check)
{
if (myarray.All<X>((c) => check(c, val))) myarray.Add(val);
return myarray;
}
然后可以将其用作
List<string> test = new List<string> { "1", "2", "3", "4", "5" };
test.AddIf<string>("1", (a, b) => !a.Equals(b)); //this will not be added
test.AddIf<string>("6", (a, b) => !a.Equals(b)); //this will be added
或者在你的情况下,
collectionToBeExtended.AddIf((c, tag) => !c.Id.Equals(tag.Id) && !c.Name.Equals(tag.Name))
确保将扩展方法放在静态类中。
答案 2 :(得分:2)
您可以为IEquatable<T>
Tag
public class Tag : IEquatable<Tag>
{
public int Id { get; set; }
public string Name { get; set; }
public bool Equals(Tag other)
{
return Id == other.Id && Name == other.Name;
}
...
}
然后你的代码将是
private ICollection<T> AddTo ( IEnumerable<T> toBeAdded , ICollection<T> collectionToBeExtended )
{
if ( collectionToBeExtended == null )
{
collectionToBeExtended = new List<T> ( );
}
if ( toBeAdded != null )
{
foreach ( T t in toBeAdded )
{
if (!collectionToBeExtended.Contains(t))
{
collectionToBeExtended.Add(t);
}
}
}
return collectionToBeExtended;
}
您必须为要将其传递给IEquatable<T>
方法的所有类实现AddTo
。并记住正确实施它。请参阅Is there a complete IEquatable implementation reference?。
还有另一种选择。只需添加另一个参数来比较两个实体,例如
private ICollection<T> AddTo<T>(IEnumerable<T> toBeAdded, ICollection<T> collectionToBeExtended, Func<T, T, bool> comparer)
{
if (collectionToBeExtended == null)
{
collectionToBeExtended = new List<T>();
}
if (toBeAdded != null)
{
foreach (T t in toBeAdded)
{
if (!collectionToBeExtended.Any(existingT => comparer(t, existingT))))
{
collectionToBeExtended.Add(t);
}
}
}
return collectionToBeExtended;
}
然后您将此方法称为AddTo(tags, collection, (t1, t2) => t1.Id == t2.Id && t1.Name == t2.Name);
。如果任何一个集合都有大量数据,则应考虑性能。
答案 3 :(得分:1)
如果您在rm -rf Capstone\ Blog/
中实施了IEquatable<T>
以及您使用此方法的任何其他类,并在方法中添加了通用约束Tag
,那么您现在可以扫描集合中的现有成员和比较它们是否相等。
但您只是复制where T:IEquatable<T>
中已存在的代码,那么为什么不将该类与构造函数HashSet<T>
一起使用?