Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple generic types in same list and calling their methods

Tags:

c#

generics

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?

like image 582
Netfangled Avatar asked Jan 15 '23 17:01

Netfangled


2 Answers

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();
    }
}
like image 124
Lee Avatar answered Jan 23 '23 05:01

Lee


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>;
}
like image 30
Brian S Avatar answered Jan 23 '23 04:01

Brian S