'if'キーワードは悪であり、ifの代わりに述語を使用する方がよいと読みました。それから私はグーグルで検索しましたが、それでも取得できません。
誰かが親切に例を提供できますか?
彼らが何を言おうと、悪でなければ。if(または一連のif)よりも述語の方が適している特定のケースがあるかもしれません。
例えば、
foreach (Foo f in fooList) {
if (f.Equals(fooTarget)) {
return f;
}
}
対(.NET 2.0)
fooList.Find(delegate (Foo f) { return f.Equals(fooTarget); });
または(後で)
fooList.Find(f => f.Equals(fooTarget));
それらはただ違います。述語は、単純なifステートメントよりも複雑です。述語は基本的に、パラメータとして受け取り、true / falseを返す型に関連付けられたメソッド(デリゲート)へのポインターです。
ジェネリックスを使用していると想像してください。ジェネリックスリストのfindメソッドのように、リストを初期化する前に、リストにどのタイプがあるかをどのように知ることができますか。したがって、findメソッドは述語を使用するだけで、述語がどのように実装されるかはわかりません。
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);
}
この場合、述語が使用されますが、(p(enumerator.Current))が実際にenumerator.Currentについて評価するものは、述語の実装中に決定されます。コードは、どのタイプTがここにあるかを認識していません。
述語をメソッドに割り当てるいくつかの方法があります
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;
}
次に、使用法は次のようになります
someList.FindAll(findShortNames1);
たとえば、次のようなループがある場合:
List<Employee> retiredEmployees = new List<Employee>();
foreach (Employee employee in EmployeeList)
{
if (employee.IsRetired)
retiredEmployees.Add(employee);
}
述語を使用すると、次のように変更する必要があります。
retiredEmployees = EmployeeList.FindAll(e => e.IsRetired);
しかし、 「ifステートメントは悪と見なされる」という議論全体で、 predicate
vsif
は、OOPおよび関数型プログラミングと手続き型プログラミングを使用する特別なケースとして言及されているだけだと思います。このパラダイムは、任意のデリゲート型 (述語だけでなく)、または OOP を使用して条件を置き換えるために簡単に一般化できます。
たとえば、コードを調べてみると、次のようなコードを簡単に見つけることができます。
public class Employee
{
private bool _isRetired;
private double _amount;
public double GetPayAmount()
{
if (_isRetired)
return _amount * 0.9;
else
return _amount;
}
}
純粋な OOP の支持者は、すぐに別のタイプの従業員を抽出し、各ブランチを別のサブタイプとして処理する必要があることを教えてくれます。これにより、「悪意のある if ステートメント」が削除されます。
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;
}
}
この方法の方がコードの保守は簡単ですが、2 番目のケースのコードの量は明らかに 2 倍になります。このような単純な階層の場合、この段階でリファクタリングを行う必要はほとんどありません。さらに多くの特殊なケースが必要であると判断した場合、条件が複雑になりすぎて、後で簡単にリファクタリングできます。
ループ構造の必要性もなくなるため、内部検索以外の単純な「if...else」構造には使用しません。例えば
int index = this.listObjects.FindIndex(x => x.PropertyA == objectItem.PropertyA);
また
List<ClassA> listClass = new List<ClassA>();
//... some more code filling listClass with ClassA types ...
ClassA tempClassA = listClass.FirstOrDefault().Where(x=> x.PropertyA == someValue);
ただし、1つのアイテムに対して実行する単純な比較がある場合は、「if...else」構文を使用することを認めなければなりません。
static void Main()
{
string[] names = { "Lukasz", "Darek", "Milosz" };
foreach (var item in names)
{
if (ContainsL(item))
Console.WriteLine(item);
}
string match1 = Array.Find(names, delegate(string name) { return name.Contains("L"); });
//or
string match2 = Array.Find(names, delegate(string name) { return name.Contains("L"); });
//or
string match3 = Array.Find(names, x => x.Contains("L"));
Console.WriteLine(match1 + " " + match2 + " " + match3); // Lukasz Lukasz Lukasz
}
static bool ContainsL(string name) { return name.Contains("L"); }