同步两个枚举

时间:2012-07-20 10:53:45

标签: c# linq synchronization ienumerable

我有两个系列。

  var a = new List<string>() { "a", "b", "c", "d", "e", "f", "j" };
  var b = new List<string>() { "a", "c", "d", "h", "i" };

我希望对该项目采取一些措施,以防它在一个或另一个集合中丢失。

public static Synchronize<T>(IEnumerable<T> first, IEnumerable<T> second, Action<T> firstSynchronizer, Action<T> secondSynchronizer)
{
  var firstUnique = first.Distinct();
  var secondUnique = second.Distinct();
  foreach (var item in firstUnique)
  {
    if (!secondUnique.Contains(item)) firstSynchronizer(item);
  }
  foreach (var item in second.Distinct())
  {
    if (!firstUnique.Contains(item)) secondSynchronizer(item);
  }
}

这是我得到的,但我对它不满意。我不禁想知道是否有更好的方法来实现这一点,因为我认为Distinct()是非常大的性能影响,而且我不确定迭代整个第二个Enumerable是否更好并检查项目是否不存在已经在第一个Enumerable中(如上所述)或者如果迭代second.Except(first)会更好吗?你们觉得怎么样?

我称之为:

  var a = new List<string>() { "a", "b", "c", "d", "e", "f", "j" };
  var b = new List<string>() { "a", "c", "d", "h", "i" };
  Synchronize(a.ToArray(), b.ToArray(), t => b.Add(t), t => a.Add(t));

我调用ToArray(),因此在迭代过程中不会更改集合,而lambdas只是将缺少的元素添加到相应的列表中。

此外,这只是一个测试实现。在生产环境中,Enumerables不会是同一类型。这旨在用于同步远程和本地存储。将来,Enumerable将首先是ICollection<DummyRemoteItem>,而Enumerable将是List<IO.FileSystemInfo>。但我希望它更通用。为了能够使用不同的集合,我想我会提出另一个类型参数和Func<T1, T2, bool>来比较项目。那将是一种最好的方法,对吗?

通常,实现

内部的最佳方式是什么
Synchronize<T>(IEnumerable<T> first,IEnumerable<T> second,Action<T> firstSynchronizer,Action<T> secondSynchronizer) 

Synchronize<TFirst, TSecond>(IEnumerable<TFirst> first,IEnumerable<TSecond> second,Action<TFirst> firstSynchronizer,Action<TSecond> secondSynchronizer, Func<TFirst, TSecond, bool> predicate)

4 个答案:

答案 0 :(得分:0)

您可以使用ExceptIntersect方法查找两个可枚举来源之间的差异或相同的项目。 MSDN在ExceptIntersect上都有很多资源。

至于关于Distinct()性能价格昂贵的评论,我会怀疑点击量很小,尝试优化会过早地这样做。

答案 1 :(得分:0)

我想最优雅的方式是使用套装。在.NET中,HashSet可能就是你要找的东西,

http://msdn.microsoft.com/en-us/library/bb495294.aspx

答案 2 :(得分:0)

Linq full outer join是你的朋友。

这是一个实现(来自here

public static IEnumerable<Tuple<T1, T2>> FullOuterJoin<T1, T2>
   (this IEnumerable<T1> one, IEnumerable<T2> two, Func<T1,T2,bool> match)
{
 var left = from a in one
   from b in two.Where((b) => match(a, b)).DefaultIfEmpty()
   select new Tuple<T1, T2>(a, b);

 var right = from b in two
   from a in one.Where((a) => match(a, b)).DefaultIfEmpty()
   select new Tuple<T1, T2>(a, b);

 return left.Concat(right).Distinct();
}

这样:

a.FullOuterJoin(b,a=>a,b=>b,(a,b)=>new {a,b})

并在生成的可枚举中查找空值。

答案 3 :(得分:0)

如果集合中的项目属于两种不同类型,则可以使用以下内容:

 class CollectionSynchronizer<TSource, TDestination>
    {
        public Func<TSource, TDestination, bool> CompareFunc { get; set; }
        public Action<TDestination> RemoveAction { get; set; }
        public Action<TSource> AddAction { get; set; }
        public Action<TSource, TDestination> UpdateAction { get; set; }

        public void Synchronizer(ICollection<TSource> sourceItems, ICollection<TDestination> destinationItems)
        {
            // Remove items not in source from destination
            RemoveItems(sourceItems, destinationItems);

            // Add items in source to destination 
            AddOrUpdateItems(sourceItems, destinationItems);
        }

        private void RemoveItems(ICollection<TSource> sourceCollection, ICollection<TDestination> destinationCollection)
        {
            foreach (var destinationItem in destinationCollection.ToArray())
            {
                var sourceItem = sourceCollection.FirstOrDefault(item => CompareFunc(item, destinationItem));

                if (sourceItem == null)
                {
                    RemoveAction(destinationItem);
                }
            }
        }

        private void AddOrUpdateItems(ICollection<TSource> sourceCollection, ICollection<TDestination> destinationCollection)
        {
            var destinationList = destinationCollection.ToList();
            foreach (var sourceItem in sourceCollection)
            {
                var destinationItem = destinationList.FirstOrDefault(item => CompareFunc(sourceItem, item));

                if (destinationItem == null)
                {
                    AddAction(sourceItem);
                }
                else
                {
                    UpdateAction(sourceItem, destinationItem);
                }
            }
        }
    }

用法如下:

var collectionSynchronizer = new CollectionSynchronizer<string, ContentImageEntity>
            {
                CompareFunc = (communityImage, contentImage) => communityImage == contentImage.Name,
                AddAction = sourceItem =>
                {
                    var contentEntityImage = _contentImageProvider.Create(sourceItem);
                    contentEntityImages.Add(contentEntityImage);
                },
                UpdateAction = (communityImage, contentImage) =>
                {
                    _contentImageProvider.Update(contentImage);
                },
                RemoveAction = contentImage =>
                {
                    contentEntityImages.Remove(contentImage);
                }
            };

            collectionSynchronizer.Synchronizer(externalContentImages, contentEntityImages);