2016-03-18 2 views
0

Я пытаюсь добавить расширение общего назначения TopN IEnumerable<T>.TopN as IEnumerable extension

Если параметр положительный, то он совпадает с Take(), но если он отрицательный, то он должен делать то же самое, что и Take(), но затем сохраняет сразу же последовательные значения, соответствующие последнему значению из Take(). (То же, что SQL ТОП н с галстуками)

Это код, у меня есть на данный момент: -

public static class Test 
{ 
    public static IEnumerable<TSource> TopN<TSource>(this IEnumerable<TSource> source, int topN) 
    { 
     return TopN(source, topN, (v1, v2) => v1.Equals(v2)); 
    } 

    public static IEnumerable<TSource> TopN<TSource>(this IEnumerable<TSource> source, int topN, Func<TSource, TSource, bool> comparer) 
    { 
     if (source == null) throw new ArgumentNullException(nameof(source)); 
     if (comparer == null) throw new ArgumentNullException(nameof(comparer)); 

     return topN >= 0 
      ? source.Take(topN) 
      : TopNWithTiesIterator(source, -topN, comparer); 
    } 

    static IEnumerable<TSource> TopNWithTiesIterator<TSource>(this IEnumerable<TSource> source, int topN, Func<TSource, TSource, bool> comparer) 
    { 
     var lastItem = default(TSource); 

     foreach (var item in source) 
     { 
      if (topN-- > 0 || comparer(item, lastItem)) 
      { 
       lastItem = item; 

       yield return item; 
      } 
      else 
      { 
       yield break; 
      } 
     } 
    } 
} 

и вот пример реального мира использования и некоторые другие быстрые тесты я попытался :

 if (TopN != 0) 
     { 
      var values = new[] { 1, 2, 2, 3 }; 
      Debug.Assert(!values.TopN(0).Any()); 
      Debug.Assert(!values.TopN(0, (v1, v2) => v1 == v2).Any()); 

      Debug.Assert(values.TopN(1, (v1, v2) => v1 == v2).Count() == 1); 
      Debug.Assert(values.TopN(-1, (v1, v2) => v1 == v2).Count() == 1); 

      Debug.Assert(values.TopN(2, (v1, v2) => v1 == v2).Count() == 2); 
      Debug.Assert(values.TopN(-2, (v1, v2) => v1 == v2).Count() == 3); 

      Debug.Assert(values.TopN(2).Count() == 2); 
      Debug.Assert(values.TopN(-2).Count() == 3); 

      // This is how I really want to use it 
      summaries = summaries.TopN(TopN, (v1, v2) => v1.ClientValue + v1.AdviserValue == v2.ClientValue + v2.AdviserValue); 
     } 

Мой вопрос о том, с помощью Func<TSource, TSource, bool>, как сравнивающий правильно.

Должен ли я использовать IEqualityComparer<T> или IEquatable<<T> или что-то еще?

+0

Попытка изменить этот вопрос, чтобы он не был основан на мнениях. Помогло бы это, если бы я добавил «.. соответствовать существующим стандартам Microsoft»? –

ответ

0

Нет выбора между Func<TSource, TSource, bool> и IEqualityComparer<T>. Вы используете Equals здесь (v1, v2) => v1.Equals(v2).

Так что это зависит от того, хотите ли вы обеспечить гибкость для предоставления пользовательского сопоставителя либо через Func<>, либо напрямую в качестве необязательного аргумента метода.

+0

Хорошая точка. Я пришел к выводу, что мне определенно нужен параметр Func, потому что у меня в настоящее время есть анонимный тип, поэтому я не могу реализовать IEquatable или IEqualityComparer. –

1

По умолчанию и ожидаемое поведение должно быть:

  • Если нет IEqualityComparer<T> не предусмотрено, что ваш метод должен проверить, если TSource реализует IEquatable<T> и использовать IEquatable<T>.Equals(T). В противном случае он должен использовать Object.Equals.

  • Если имеется IEqualityComparer<T>, то используйте его.

  • Если предикат предоставляется для имитации поведения IEqualityComparer<T>, используйте его вместо IEqualityComparer<T>.

+0

См. Комментарий ниже. Я думаю, что анонимный тип означает, что параметр Func должен остаться, но я также буду использовать ваши предложения, чтобы использовать IEquatable/Object.Equals, а не мои упрощенные Equals. Спасибо вам обоим. –

+0

@SimonHewitt Да, вы можете предоставить много перегрузок. Но если вы хотите иметь «полное» решение, вы должны следовать критериям в моем ответе –

Смежные вопросы