How to pass a generic selector Func<T, U> to some method in order to perform search?

Viewed 169

I have a class with some fields.

public abstract class RegisteredUser : Transformable //implements interface
{
    protected int id;
    protected bool active;
    protected string name;
    protected string lastName;
    protected string email;
    protected string username;
    protected string password;
    protected int institution;

//OTHER  STUFF FROM  THIS CLASS GO HERE..`
} 
Assistant : RegisteredUser
{
}

Once I load all data from .txt file, I'll have a static dictionary like this:

public static Dictionary<int, Transformable> assistants = null; //this will be filed with objects and their id's

Now, my assignment is to achieve functionality of search for this console app. Since, all of sub classes will have fields from abstract class, user of this app should have a choice to search for any object Assistant, Professor or Administrator by any of its fields.

I would like to achieve this by using LINQ/lambdas. My idea is to create some generic method which will accept a selector (or some lambda expression ), based on whatever field is being searched for.

I am completely new to LINQ and C# and generally to programming, so I hope that I'm not talking nonsense here.

public static List<RegisteredUser> search(Dictionary<int, Transformable> map, 
                                          Func<bool,RegisteredUser> selector, 
                                          Transformable userType)
{
    if (userType is Assistant)
    {
        List<RegisteredUser> list = map.Select(item => (Assistant) item.Value)
                                       .ToList()
                                       .Select(selector)
                                       .ToList();
    }
    //other if/else statements  

    return list;
}

Of course, lines in Visual Studio are red :) Basically, I want to be be able send something like user => user.Name.Contains("some string which will be entered by the end user") to generic function.

1 Answers

You don't really need to write a separate function to do what you want to achieve, it can all be done with Linq directly:

var users = new Dictionary<int, Transformable>
{
     {0, new Assistant { Name = "Joe" }},
     {1, new Professor { Name = "John" }},
     {2, new Assistant { Name = "Jane" }}
};

var assistants = users.Values.OfType<Assistant>().Where(a => a.Name == "Jane");

assistants is an IEnumerable<Assistant> with Name equal to "Jane" and will contain only 1 entry from the above dictionary. You could add a ToList() to the end of that to create list alternatively

You can then add whatever predicate you want:

var assistants = users.Values.OfType<Assistant>().Where(a => a.Name == "Jane" && a.LastName == "Smith" && a.Active);

You could wrap the above in a function but what you'll end up with won't be much shorter than the above.

As an extension method it would be like this:

public static class Extensions
{
    public static IEnumerable<T> Find<T>(this IEnumerable<Transformable> list, Func<T,bool> predicate) where T :Transformable
    {
        return list.OfType<T>().Where(predicate.Invoke);
    }
}

making the call slightly shorter:

var assistants = users.Values.Find<Assistant>(a => a.Name == "Jane");

This is an example of the fluent API pattern so you can use it in a Linq chain like any other Linq function.

Related