Reputation: 2629
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
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
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