如何通过C#中的泛型编程来管理这种情况?

时间:2016-04-30 15:07:08

标签: c# generics

我有下面的代码,它在我的代码中的两个位置实现了我需要的逻辑。代码以通用方式编写。

它有缺陷。它不检查给定对象是否已存在于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;
    }

4 个答案:

答案 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>一起使用?