Maro
Maro

Reputation: 2629

Pass property name as parameter

I have the following class

public class School
{
    public List<Student> Students { get; set; }
    public List<Teacher> Teachers { get; set; }
}

Now i have this method

public bool Evaluate(??)
{
    var school = DbContext.Schools.FirstOrDefault();
    return school.??.Any(/*some expresions*/)
}

I should be able to pass a value in ?? and use it so that i can use both

return school.Students.Any(/*some expresions*/)
return school.Teachers.Any(/*some expresions*/)

So how can i replace the question marks with Students or Teachers ?

Edit:

public class Student 
{
    public string FullName { get; set; }
    public bool Registered { get; set; }
    public bool Passed { get; set; }
}

public class Teacher
{
    public string FullName { get; set; }
    public bool CanEvaluate { get; set; }
    public bool Validator { get; set; }
}


public class DynamicCheckTest
{
    public bool MyExpression<T>(List<T> items, string name,
        Expression<Func<T, bool>> expression)
    {
        return items.Any(x => expression.Compile()(x));
    }
}

public static bool Check<T>(this List<T> items, Func<T, bool> compiledExp)
{
    return items.Any(x => compiledExp(x));
}

Students.Check(x => x.Name == "Mike" &&  x.Registered); // example
Teachers.Check(x => x.Name == "Jack" &&  x.CanEvaluate);// example

Now i have to pass the school along which contains both Students and Teachers But i don't know which one will be called in advance

Upvotes: 0

Views: 1014

Answers (2)

Lance U. Matthews
Lance U. Matthews

Reputation: 16596

Addressing the "Pass property name as parameter" request, you could use reflection for that, but I don't think that's a good way to go. Instead, a Func<School, List<TElement>> could be used to select the desired List<> property to evaluate...

public bool Evaluate<TElement>(Func<School, List<TElement>> listSelector)
    where TElement : Person
{
    School school = DbContext.Schools.FirstOrDefault();
    DateTime today = DateTime.Today;

    return listSelector(school)
        // For example, check if today is the birthday of anyone in the selected list
        .Any(person => person.DateOfBirth.Month == today.Month && person.DateOfBirth.Day == today.Day);
}

As @Enigmativity points out, the type constraint is necessary in order to pass much of a meaningful condition to Any(), which also assumes/requires that Student and Teacher have common ancestry, like this...

public abstract class Person
{
    public DateTime DateOfBirth
    {
        get;
    }
}

public class Student : Person
{
}

public class Teacher : Person
{
}

You'd then use a lambda expression to specify the desired List<>...

bool isAnyStudentsBirthday = Evaluate(school => school.Students);
bool isAnyTeachersBirthday = Evaluate(school => school.Teachers);

This will work as long as the members you want Any() to consider are available in the constrained type (i.e. Person). If you wanted to filter using members specific to the Student or Teacher class, your best bet would be to use an approach like @Enigmativity's answer, where the filter itself is a parameter and receives the same derived type as the selected List<> stores.

Note that if you ever want to use Evaluate() with some other collection property of School that is not specifically List<>, or just knowing that all Any() needs is an IEnumerable<>, you could change the return type (last type parameter) of the Func<> to something less-restrictive...

Func<School, IList<TElement>>
Func<School, ICollection<TElement>>
Func<School, IEnumerable<TElement>>

Upvotes: 2

Enigmativity
Enigmativity

Reputation: 117010

You could use this method:

public bool Evaluate<T>(Func<School, List<T>> project, Func<T, bool> filter)
{
    var school = DbContext.Schools.FirstOrDefault();
    return project(school).Any(filter);
}

If we assume that the implementation of Student and Teacher are this:

public class Student
{
    public string Name;
}

public class Teacher
{
    public string Subject;
}

Then you could do this:

bool hasFred = Evaluate(school => school.Students, student => student.Name == "Fred Nerk");
bool teachArt = Evaluate(school => school.Teachers, teacher => teacher.Subject == "Art");

Upvotes: 2

Related Questions