tags:

views:

148

answers:

5

I have a generic class that needs to limit an enum depending on the type defined:

public enum ConditionOperatorsString { None, Like, Equal }
public enum ConditionOperatorsDate { None, Equal, BeforeThan, AfterThan }
public class Condition<T>
{
  public T Value { get; set; }
  public ConditionOperatorsString Operator { get; set; }
  public Condition(T Value, ConditionOperatorsString Operator)
  {
    this.Value = Value;
    this.Operator = Operator;
  }
}

Now the problem is that i want the Operator type be dependant on T so when:

Condition<string> CStr= new Condition<string>(string.Empty, ConditionOperatorsString.None)
Condition<DateTime> CStr= new Condition<DateTime>(DateTime.Now, ConditionOperatorsDate.None)

How do I define the class Condition for that? I thought of an interface but enums don't inherit from interfaces.

A: 

If you make your field

public ValueType Operator { get; set; }

It'll work, but I'm not sure if that's what you want ...

-- Edit

What are you trying to do, anyway? C# has operator overloading, so perhaps that could be of use, instead?

Noon Silk
What i am trying is to restrict the enums values i receive depending on the type my condition<T> is.From a business-like perspective my condition represent a filter on the gui. So that i cannot have a before or after for a condtion that filters a string, only equal and likeLikewise i cannot accept like for a date, only equal, before or after.The ValueType will accept anything as value wich is the opposite of what i am trying to achieve.
I'd probably go a different way. I'd have various comparators, and have them list what sort of methods they allow by having them implement various interfaces (IStringCompare, etc).
Noon Silk
A: 

Could the enum type just be another generic parameter e.g.

public enum ConditionOperatorsString { None, Like, Equal }
public enum ConditionOperatorsDate { None, Equal, BeforeThan, AfterThan }
public class Condition<T, TEnum>
{
  public T Value { get; set; }
  public TEnum Operator { get; set; }
  public Condition(T Value, TEnum Operator)
  {
    this.Value = Value;
    this.Operator = Operator;
  }
}

Might need more info though, not 100% sure how this is meant to actually fit in with the code that will make use of it

Grant Peters
This doesn't create any restriction that forces TEnum to be ConditionOperationsString when T is a string, or ConditionOperatorsDate when T is a DateTime.
Nader Shirazie
Who is this meant to restrict, the programmer or end user?
Grant Peters
I'd expect the programmer (and thus the user), because of "want the Operator type be dependant on T". It might just be a lot simpler to trust the programmer.
Nader Shirazie
It is suposed to restrict the programmer of the client app.
+1  A: 

There's no good way to do what you're trying to do without having, in your constructor something like:

if (typeof(T) == typeof(string) && 
    typeof(Operator) != typeof(ConditionOperatorsString))
{ 
    throw new Exception("Invalid conditionoperators value);
}

That's really not very useful, since you have to know, ahead of time, all the different possibilities for T.

What you could do, is something like this:

public abstract class ComparableBase<T,K>
{
    public T Value { get; protected set; }
    public K ConditionOperator { get; protected set; }
    // public abstract bool IsMatch(T other); // Is this required?

    protected ComparableBase(T value, K op)
    {
        this.Value = value;
        this.ConditionOperator = op;
    }
}

public enum ComparableOperator { None, Equal, Less, Greater }

public enum LikeOrEqualOperator { None, Like, Equal }

public class ComparableCondition<T> : ComparableBase<T,ComparableOperator>
{
    public ComparableCondition(T value, ComparableOperator op):base(value, op)
    {
    }
}

public class LikeOrEqualCondition<T> : ComparableBase<T, LikeOrEqualOperator>
{
    public LikeOrEqualCondition(T value, LikeOrEqualOperator op):base(value, op)
    {
    }
}

then you can declare

var condition1 = new LikeOrEqualCondition<string>("hi", LikeOrEqualOperator.Equal);

Do you need to have an IsMatch? Or is this to display the selected filter, without actually implementing it.

If you do, things are a teensy bit more complicated...

Nader Shirazie
A: 

You should include the enum type as a template parameter.

public class Condition<TValue, TOperator> 
{
    public TValue Value { get; private set; }
    public TOperator Operator { get; private set; }

    private Condition(TValue val, TOperator op)
    {
        Value = val;
        Operator = op;
    }
}

It would be nice to be able to add where TOperator : Enum, but I'm pretty sure the compiler doesn't allow that.

JSBangs
A: 

Thanks for the answers.

I wanted to avoid to have more than one class but apparently it cannot be done. Based on the last comment from silky i got to a similar sollution as nader. Not the optimal one but it does the trick of restricting the enum, the con is that the client has to be aware that there are n classes:

public abstract class ConditionBase<T, E>
{
  public T Value { get; set; }
  public E Operator { get; set; }
  protected ConditionBase(T Value, E Operator)
  {
    this.Value = Value;
    this.Operator = Operator;
  }
}

public enum ConditionOperatorsString { None, Like, Equal }
public class ConditionString : ConditionBase<string, ConditionOperatorsString>
{
  public ConditionString(String Value, ConditionOperatorsString Operator) : base(Value, Operator) { }
}
public enum ConditionOperatorsDate { None, Like, BeforeThan, AfterThan }
public class ConditionDate : ConditionBase<DateTime?, ConditionOperatorsDate>
{
  public ConditionDate(DateTime? Value, ConditionOperatorsDate Operator) : base(Value, Operator) { }
}

Client use:

  ConditionString StrCond = new ConditionString(string.Empty, ConditionOperatorsString.None);
  ConditionDate DateCond = new ConditionDate(DateTime.MinValue, ConditionOperatorsDate.None);

I wanted to be able to use from the client something like:

  ConditionGeneric StrCond = new ConditionGeneric(string.Empty, ConditionOperatorsString.None);
  ConditionGeneric DateCond = new ConditionGeneric(DateTime.MinValue, ConditionOperatorsDate.None);
  ConditionGeneric InvalidStrCond = new ConditionGeneric(string.Empty, ConditionOperatorsDate.None);
  ConditionGeneric InvalidDateCond = new ConditionGeneric(DateTime.MinValue, ConditionOperatorsString.None);