views:

50

answers:

1

The name is a little blurry, so here's the situation:

I'm writing code to use some 'trajectories'. The trajectories are an abstract thing, so I describe them with different interfaces. So I have a code as this:

namespace Trajectories {
    public interface IInitial<Atom>
    {
        Atom Initial { get; set; }
    }

    public interface ICurrent<Atom>
    {
        Atom Current { get; set; }
    }

    public interface IPrevious<Atom>
    {
        Atom Previous { get; set; }
    }

    public interface ICount<Atom>
    {
        int Count { get; }
    }

    public interface IManualCount<Atom> : ICount<Atom>
    {
        int Count { get; set; }
    }
    ...
}

Every concrete implementation of a trajectory will implement some of the above interfaces. Here's a concrete implementation of a trajectory:

    public class SimpleTrajectory<Atom> : IInitial<Atom>, ICurrent<Atom>, ICount<Atom>        {
        // ICount
        public int Count { get; private set; }

        // IInitial
        private Atom initial;
        public Atom Initial { get { return initial; } set { initial = current = value; Count = 1; } }

        // ICurrent
        private Atom current;
        public Atom Current { get { return current; } set { current = value; Count++; } }
    }

Now, I want to be able to deduce things about the trajectories, so, for example I want to support predicates about different properties of some trajectory:

namespace Conditions
{
    public interface ICondition<Atom, Trajectory>
    {
        bool Test(ref Trajectory t);
    }

    public class CountLessThan<Atom, Trajectory> : ICondition<Atom, Trajectory>
        where Trajectory : Trajectories.ICount<Atom>
    {
        public int Value { get; set; }
        public CountLessThan() { }

        public bool Test(ref Trajectory t)
        {
            return t.Count &lt Value;
        }
    }

    public class CurrentNormLessThan<Trajectory> : ICondition<Complex, Trajectory>
        where Trajectory : Trajectories.ICurrent<Complex>
    {
        public double Value { get; set; }
        public CurrentNormLessThan() { }

        public bool Test(ref Trajectory t)
        {
            return t.Current.Norm() &lt Value;
        }
    }
}

Now, here's the question: What if I wanted to implement AND predicate? It would be something like this:

public class And<Atom, CondA, TrajectoryA, CondB, TrajectoryB, Trajectory> : ICondition<Atom, Trajectory>
    where CondA : ICondition<Atom, TrajectoryA>
    where TrajectoryA : // Some interfaces
    where CondB : ICondition<Atom, TrajectoryB>
    where TrajectoryB : // Some interfaces 
    where Trajectory : // MUST IMPLEMENT THE INTERFACES FOR TrajectoryA AND THE INTERFACES FOR TrajectoryB 
{
    public CondA A { get; set; }
    public CondB B { get; set; }

    public bool Test(ref Trajectory t) {
        return A.Test(t) && B.Test(t);
    }
}

How can I say: support only these trajectories, for which the arguments of AND are ok?

So I can be able to write:

var vand = new CountLessThan(32) & new CurrentNormLessThan(4.0);

I think if I create an overall interface for every subset of interfaces, I could be able to do it, but it will become quite ugly.

+1  A: 

You could eliminate ICondition entirely and just use Predicates and functional style. Here's a sample static way to make one:-

public static Predicate<T> CountLessThan<T> (int value) where T:ICount
{ 
    return (T item) => item.Count < value;
}

And here's how you might use it:-

Predicate<SimpleTrajectory> anded = x => CountLessThan<SimpleTrajectory>(5)(x) && CountLessThan<SimpleTrajectory>(3)(x);

You could also create extension methods like this to combine predicates:-

public static Predicate<T> And<T>(this Predicate<T> first, Predicate<T> second)
{
    return (T item) => first(item) && second(item);
}

An alternative would be to have a base, abstract, generic Condition class from which your other conditions derive and then implement And, Or, ... in the base condition class allowing for a fluent interface like:-

 ... = new CountLessThan(5).And(new CountLessThan(3))

Being generic in T would ensure that you could only combine conditions that have the same T.

Hightechrider
But the problem is still there:CountLessThan<T> requires T : ICount, but CurrentNormLessThan<T> requires T : ICurrent, so these are different T's. Your code will look like this:Predicate<Both> And<Both>(this Predicate<T1> first, Predicate<T2> second) where T1 : ICount where T2 : ICurrent where Both : ICount, ICurrent;The And should accept only those classes, which implement the union of the interfaces, required by the 2 arguments.
K. Georgiev
`And<Both>` works if you actually have a `Both` class that implements both interfaces, but what you want is a Predicate that can be used with a BothA or a BothB provided they implement the two interfaces mentioned in building the And predicate, right? not one that's tied to just `Both`. I see ...
Hightechrider
Sorry, `And` wasn't meant to have the interface on it, all it needs is two Predicates of the same `T` to work. Fixed.
Hightechrider