I'm attempting to write a fluent interface which is able to, at design time, detect the property type being passed as a Linq Expression and return an interface based on an evaluation of that type.
For example:
public class Searchable<Person>
{
public Searchable()
{
CanSearch(x => x.Name)
.Include(StringOperations.Contains);
CanSearch(x => x.Age)
.Exclude(NumericOperators.GreaterThan);
}
}
Within the CanSearchMethod, I'd like to be able to do something like:
public IOperations CanSearch(Expression<Func<T, object>> expression)
{
var type = (/* Code to pick apart expression */);
if(type == typeof(int))
return new ClassImplementingINumericOperations();
if(type == typeof(string))
return new ClassImplementingIStringOperations();
if(type == typeof(IEnumerable))
return new ClassImplementingIIEnumerableOperations();
return null;
}
The Include and Exclude methods for the different interfaces differ only by an enum accepted as the argument:
public interface IOperations
{
}
public interface INumericOperations : IOperations
{
INumericOperations Include(NumericOperationsEnum op);
INumericOperations Exclude(NumericOperationsEnum op);
}
public interface IStringOperations : IOperations
{
IStringOperations Include(StringOperationsEnum op);
IStringOperations Exclude(StringOperationsEnum op);
}
public interface IIEnumerableOperations : IOperations
{
IIEnumerableOperations Include(CollectionOperationsEnum op);
IIEnumerableOperations Exclude(CollectionOperationsEnum op);
}
I suspect this is not possible but I can't it out completely since dynamics can do some funky magic.
I've looked at some other fluent interfaces but none seem to evaluate the expression in order to determine what types to return while designing.
You can do this with compile-time type-safety using overloads:
public IStringOperations CanSearch<T>(Expression<Func<T, string>> expression);
public IIEnumerableOperations<TItem> CanSearch<T, TItem>(Expression<Func<T, IEnumerable<TItem>> expression);
However, for numeric types, you'll either need a separate overload for each of the 7 numeric types, or a generic INumericOperations<TNumber>
which will be annoyingly inflexible.