IEnumerable, где All встречает X, и по крайней мере один встречает y - PullRequest
0 голосов
/ 10 февраля 2011

у меня 2 IEnumerable<decimal>.например, {0,0.1,0.5,1} и {a,b,c,d} предполагают равную длину

Пример кода объекта домена:

public class Foo //does not implement IEnumerable because nothing outside of itself should treat it as such
{
 private readonly decimal _a;
private readonly decimal _b;
private readonly decimal _c;
//another class also has private readonly  decimal _d;

public decimal A {get{return _a;}}
//...
public decimal C {get{return _c;}}
}

Я хочу определить Foo1>Foo2

  • , если всевстретить >= (как в Foo1.A>=Foo2.A && ' .. 'Foo1.C>=Foo2.C ..)
  • Как минимум один > (как в Foo1.B>Foo2.B)

Пример итерационного кода:

//DRY violation, but probably the shortest route to the goal
private static IEnumerable<Func<Foo,decimal>> Accessors=
 new List{f=>f.A,f=>f.B,f=>f.C};

public static bool operator>(Foo foo1, Foo foo2)
{
  if (foo1==null||foo2==null)
    return false;
  bool foundGreater=false;
  foreach (var accessor in _Accessors)
  {
    if (accessor(foo1)<accessor(foo2))
      return false;
    if (foundGreater==false&&accessor(foo1)>accessor(foo2))
      foundGreater=true;
  }
  return foundGreater;
}

информация и ответы, включающие zip, приветствуются с точки зрения обучения, так как они атакуют всю проблему переменных длин свойств, используя те же функции сравнения, которые не требуют отражения.

Однако в настоящее время я нахожусьработа в рамках .net 2.0 с LinqBridge.

Я рассматриваю что-то вроде следующего, чтобы охватить все классы, которым требуется одинаковая функциональность

  //Needs a better name for sure
  public static bool AllWithAny<T,TValue>(IEnumerable<Func<T,TValue>> accessors,T item1, T item2,
      Func<TValue,TValue,bool> shortCircuitBreak,Func<TValue,TValue,bool> atLeastOneCondition)
    {

      GuardStrategy.ThrowIfNull(accessors,"accessors");
      GuardStrategy.ThrowIfNull(item1, "item1");
      GuardStrategy.ThrowIfNull(item2, "item2");
      var foundOne=false;
      foreach(var accessor in accessors) 
      {
        var values=new {Value1=accessor(item1),Value2=accessor(item2)};
        if (shortCircuitBreak(values.Value1, values.Value2))
          return false;
        if(foundOne==false && atLeastOneCondition(values.Value1,values.Value2))
         {
          foundOne=true;
         }
      }
      return foundOne;

    }

Вопрос (ы):

Существует ли существующая комбинация ключевых слов / операторов Linq, которая сделает все это более изящно?Есть ли более элегантный / более простой способ сделать этот тип сравнения, который лучше DRY, меньше кодирования и больше повторного использования?

Ответы [ 2 ]

1 голос
/ 10 февраля 2011

Это сработало бы (используя идею @ gaeron для предоставления IEnumerable значений для облегчения доступа)

public static bool operator >(Foo foo1, Foo foo2)
{
    if (foo1 == null || foo2 == null)
        return false;

    var zipSeq = foo1.Values.Zip(foo2.Values, (a, b) => a - b);
    bool isGreater = zipSeq.All(x => x >= 0) && zipSeq.Any(x => x > 0);

    return isGreater;
}

Основная идея состоит в том, что для foo1, превышающего foo2, вычитаниекаждого элемента в foo1.Values с соответствующим элементом в foo2.Values должно быть> = 0 И должна быть хотя бы одна запись, которая> 0.

1 голос
/ 10 февраля 2011

Не тестировался, но с небольшими изменениями он должен работать.

public class Foo {
    public decimal A { get; set; }
    public decimal B { get; set; }
    public decimal C { get; set; }

    IEnumerable<decimal> Values {
        get { return new [] { A, B, C }; }
    }

    public static bool operator > (Foo x, Foo y)
    {
        var pairs = x.Values.Zip (y.Values,
            (xv, yv) => Tuple.Create (xv, yv));

        return pairs.All (pair => pair.Item1 >= pair.Item2)
            && pairs.Any (pair => pair.Item1 > pair.Item2);
    }
}

P.S. Я не совсем понял ваш вопрос, поэтому я просто реализовал то, что вы спросили в начале.

...