Reputation: 2081
I'm making an object validation framework in my spare time to learn a few things and maybe use it for some school projects.
I have my generic Rule class, which looks something like this :
class Rule<T>
{
string propertyName;
Func<T, bool> ruleLambda;
bool IsBroken(T value)
{
return ruleLambda(value);
}
}
An object that would be validated would look a bit like this :
class Example
{
List<Rule<?>> MyRules; // can take all types of rules
List<Rule<T>> Validate<T>(string propertyName, T value)
{
List<Rule<T>> brokenRules = new List<Rule<T>>();
foreach (Rule rule in MyRules.Where(r => r.propertyName == propertyName))
{
if (rule.IsBroken(value))
brokenRules.Add(rule);
}
return brokenRules;
}
}
Where the T value
argument would be the value of one of the Example class's properties, which can be of any type.
The Validate<T>
method is called whenever a property is set.
The problem lies with the class's list of rules. Specifically the List<Rule<?>>
line above. I want to store all the rules for a given class in the same list.
Alas, C# doesn't have a wildcard for generic types like in Java.
How should I do this?
A non-generic interface or base class utilizing objects instead of T could work, but how would I call the generic Rule's IsBroken
method and not the non-generic one?
Upvotes: 4
Views: 4124
Reputation: 2081
I've tried a few things and I've found something that works pretty well for my needs. I have Rule<T>
inherit from a base abstract rule class, with a generic IsBroken
method:
abstract class Rule
{
string propertyName;
Func<object, bool> objectRule;
bool IsBroken<T>(T value)
{
Rule<T> rule = this as Rule<T>;
if (rule == null)
return objectRule(value);
return rule.IsBroken(value);
}
}
As you can see, I try to convert the base class to its generic counterpart using the generic type parameter in the IsBroken
method.
Also, when creating a Rule<T>
instance, I send a Func<object, bool>
to its base class protected constructor:
public Rule(string propertyName, Func<T, bool> ruleLambda)
: base(propertyName, ConvertToObjectFunc(ruleLambda))
{
}
With the conversion method looking like this:
static Func<object, bool> ConvertToObjectFunc(Func<T, bool> func)
{
return new Func<object, bool>(o => func((T)o));
}
However, if it can't cast o to type T, it crashes. So I wrote this... thing:
static Func<object, bool> ConvertToObjectFunc(Func<T, bool> func)
{
return new Func<object, bool>
(
o =>
{
try
{
T obj = (T)o;
return func(obj);
}
catch { return true; } // rule is broken by default
}
);
}
It's pretty ugly, but it works. Hope this can help anybody else.
Upvotes: 1
Reputation: 144206
I would store your rules as object
inside the Example
class and use Enumerable.OfType<T>
to find the matching rules for a given type:
class Example
{
private List<object> rules;
List<Rule<T>> Validate<T>(string propertyName, T value)
{
return this.rules.OfType<Rule<T>>()
.Where(r => r.PropertyName == propertyName && r.IsBroken(value))
.ToList();
}
}
Upvotes: 5
Reputation: 5785
In cases where I've needed something like this, I use interfaces or non-generic base classes. For example, you could create an interface:
public interface IRule
{
//non-generic properties & methods
}
public class Rule<T> : IRule
{
//implementation
}
then create a list of the interfaces:
private List<IRule> MyRules;
If you want to make converting from the interface to the generic easy, you could add an extension method:
public static Rule<T> ToGeneric<T>(this IRule rule)
{
return rule as Rule<T>;
}
Upvotes: 3