Several common types in the same list and call their methods

I create a framework for checking objects in my free time to learn a few things and possibly use them for some school projects.

I have my own general rule class that looks something like this:

class Rule<T> { string propertyName; Func<T, bool> ruleLambda; bool IsBroken(T value) { return ruleLambda(value); } } 

The object to be scanned looks something 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 argument T value will be the value of one of the properties of the Example class, which can be of any type.

The Validate<T> method is called whenever the property is set.

The problem is the list of class rules. In particular, the List<Rule<?>> above. I want to keep all the rules for this class in the same list.

Alas, C # does not have a wildcard for generic types, such as in Java.

How can I do it?

Not a common interface or a base class that uses objects instead of T cannot be used, but how would you call the general Rule method IsBroken , and not not the general one?

+4
source share
3 answers

I tried several things and I found something that works well for my needs. I have a Rule<T> inheritance from a base abstract class of rules using the 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 am trying to convert a base class to its generic instance using the type parameter in the IsBroken method.

In addition, when creating an instance of Rule<T> I send Func<object, bool> to my constructor, protected by the base class:

 public Rule(string propertyName, Func<T, bool> ruleLambda) : base(propertyName, ConvertToObjectFunc(ruleLambda)) { } 

Using the conversion method, it looks like this:

 static Func<object, bool> ConvertToObjectFunc(Func<T, bool> func) { return new Func<object, bool>(o => func((T)o)); } 

However, if it cannot distinguish o from type T, it will work. 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 } ); } 

This is pretty ugly, but it works. Hope this helps someone else.

+1
source

I would save your rules as an object inside the Example class and use Enumerable.OfType<T> to find the appropriate rules for this 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(); } } 
+4
source

In cases where I need something like this, I use interfaces or base classes that are not universal. For example, you can create an interface:

 public interface IRule { //non-generic properties & methods } public class Rule<T> : IRule { //implementation } 

then create a list of interfaces:

 private List<IRule> MyRules; 

If you want to make the conversion from interface to general simple, you can add an extension method:

 public static Rule<T> ToGeneric<T>(this IRule rule) { return rule as Rule<T>; } 
+2
source

All Articles