I read that the 'if' keyword is evil, and better to use predicate to replace if. Then I googled, but still dont get it.
Can anyone be kind to provide an example?
I read that the 'if' keyword is evil, and better to use predicate to replace if. Then I googled, but still dont get it.
Can anyone be kind to provide an example?
No matter what they say, if is not evil. There may be specific cases for which a Predicate is a better choice than an if (or a set of ifs).
For example,
foreach (Foo f in fooList) {
if (f.Equals(fooTarget)) {
return f;
}
}
versus (.NET 2.0)
fooList.Find(delegate (Foo f) { return f.Equals(fooTarget); });
or (later)
fooList.Find(f => f.Equals(fooTarget));
I don't use them for straight "if ... else" constructs myself, other than withinn searches, as it also removes the need for loop constructs. For example
int index = this.listObjects.FindIndex(x => x.PropertyA == objectItem.PropertyA);
or
List<ClassA> listClass = new List<ClassA>();
//... some more code filling listClass with ClassA types ...
ClassA tempClassA = listClass.FirstOrDefault().Where(x=> x.PropertyA == someValue);
I must admit though that if there's just a straight comparison to perform on one item then I use and "if ... else" construct.
They are just different. A predicate is more complex than a simple if statement. A predicate is basically a pointer to a method (delegate) which is tied to a type that it takes as a param and returns true/false.
Imagine you are using generics, and like the find method on generic lists, how can it know what types are in the list prior to your initialization of it. So the find method just uses the predicate and does not know how the predicate will be implemented.
public T Find(Predicate<T> p)
{
//iterate through the collection and return the first match
IEnumerator<T> enumerator = this.GetEnumerator();
while (enumerator.MoveNext())
{
if (p(enumerator.Current))
{
return enumerator.Current;
}
}
return default(T);
}
In this case, a predicate is used but what (p(enumerator.Current)) actually evaluates about enumerator.Current is determined during implementation of the predicate. The code is not aware of what type T wil end up being here.
Here are some ways to assign the predicate to a method
Predicate<string> findShortNames1 = x => x.Length == 3; // lambda expression
Predicate<string> findShortNames2 = delegate(string x) { return x.Length == 3; }; // anonymous method
Predicate<string> findShortNames3 = MatchOnShortNames; //existing method
// ...
private bool MatchOnShortNames(string s)
{
return s.Length == 3;
}
Then the usage is like
someList.FindAll(findShortNames1);
For example, whenever you have a loop like this:
List<Employee> retiredEmployees = new List<Employee>();
foreach (Employee employee in EmployeeList)
{
if (employee.IsRetired)
retiredEmployees.Add(employee);
}
Using a predicate, you would have to change it to:
retiredEmployees = EmployeeList.FindAll(e => e.IsRetired);
But I believe, in the whole "if statement considered evil" debate, predicate
vs if
is just mentioned as a special case of using OOP and functional programming vs procedural programming. This paradigm can be easily generalized to any delegate type (not just predicate), or any use of OOP to replace a conditional:
For example, if you go through your code, you may easily find code such as this:
public class Employee
{
private bool _isRetired;
private double _amount;
public double GetPayAmount()
{
if (_isRetired)
return _amount * 0.9;
else
return _amount;
}
}
Pure OOP supporters will tell you that you immediately need to extract a different type of employee and handle each branch as a different subtype, which will remove the "evil if statement":
public interface IEmployee
{
double GetPayAmount();
}
public class Employee : IEmployee
{
private double _amount;
public double GetPayAmount()
{
return _amount;
}
}
public class RetiredEmployee : IEmployee
{
private double _amount;
public double GetPayAmount()
{
return _amount * 0.9;
}
}
Although the code is easier to maintain this way, the amount of code in the second case has clearly doubled. For a simple hierarchy as this, there is little need to do the refactoring at this phase. If you decide that you need many more special cases, then your conditional might become too complex, and you can easily refactor it later.