2009-10-13 4 views
2

J'ai lu que le mot-clé «si» est mal, et préférable d'utiliser le prédicat pour remplacer si. Ensuite, j'ai googlé, mais je ne comprends toujours pas.Un exemple d'utilisation de prédicat pour remplacer 'if' dans C#?

Quelqu'un peut-il être aimable de donner un exemple?

+1

semble être lié à cette question: http://stackoverflow.com/questions/1554180/why-is-the-if-statement-considered-evil (pas dupe, juste contexte) –

+0

Cela pourrait aide aussi bien - http://stackoverflow.com/questions/556425/predicate-delegates-in-c –

+1

Un programme sans branchement est en effet un très désolé. –

Répondre

9

Peu importe ce qu'ils disent, si ce n'est pas mal. Il peut y avoir des cas spécifiques pour lesquels un prédicat est un meilleur choix qu'un if (ou un ensemble de ifs).

Par exemple,

foreach (Foo f in fooList) { 
    if (f.Equals(fooTarget)) { 
     return f; 
    } 
} 

contre (.NET 2.0)

fooList.Find(delegate (Foo f) { return f.Equals(fooTarget); }); 

ou (plus tard)

fooList.Find(f => f.Equals(fooTarget)); 
0

Je ne les utilise pas pour droit « si .. "else" se construit moi-même, à l'exception des recherches, car il supprime également le besoin de construire des boucles. Par exemple

int index = this.listObjects.FindIndex(x => x.PropertyA == objectItem.PropertyA); 

ou

List<ClassA> listClass = new List<ClassA>(); 

//... some more code filling listClass with ClassA types ... 

ClassA tempClassA = listClass.FirstOrDefault().Where(x=> x.PropertyA == someValue); 

Je dois admettre cependant que s'il y a juste une comparaison directe à effectuer sur un élément, puis-je utiliser et « if ... else » construire.

2

Ils sont simplement différents. Un prédicat est plus complexe qu'une simple instruction if. Un prédicat est essentiellement un pointeur vers une méthode (délégué) liée à un type qu'il prend en paramètre et renvoie vrai/faux. Imaginez que vous utilisez des génériques, et comme la méthode find sur les listes génériques, comment savoir quels types sont dans la liste avant de l'initialiser. Ainsi, la méthode find utilise simplement le prédicat et ne sait pas comment le prédicat sera implémenté.

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); 
    } 

Dans ce cas, un prédicat est utilisé, mais ce que (p (enumerator.Current)) évalue en fait de enumerator.Current est déterminé lors de la mise en œuvre du prédicat. Le code n'est pas au courant de quel type T finira par être ici.

Voici quelques façons d'attribuer le prédicat à une méthode

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; 
} 

Ensuite, l'utilisation est comme

someList.FindAll(findShortNames1); 
+0

Bonne réponse. Je ne savais pas du tout comment assigner des délégués aux prédicats. –

+0

Vous avez encore un "mal si" dans le premier exemple :) – Groo

+0

Je n'ai pas de problème avec 'si', ça sert un but – CRice

2

Par exemple, chaque fois que vous avez une boucle comme ceci:

List<Employee> retiredEmployees = new List<Employee>(); 
foreach (Employee employee in EmployeeList) 
{ 
    if (employee.IsRetired) 
     retiredEmployees.Add(employee); 
} 

En utilisant un prédicat, vous devez le changer pour:

retiredEmployees = EmployeeList.FindAll(e => e.IsRetired); 

Mais je crois, dans tout le débat "if statement considered evil", predicate vs if est juste mentionné comme un cas particulier de l'utilisation de la POO et la programmation fonctionnelle vs programmation procédurale.Ce paradigme peut être facilement généralisé à n'importe quel type de délégué (pas simplement un prédicat), ou toute utilisation de POO pour remplacer un conditionnel:

Par exemple, si vous parcourez votre code, vous pouvez facilement trouver le code suivant:

public class Employee 
{ 
    private bool _isRetired; 
    private double _amount; 
    public double GetPayAmount() 
    { 
     if (_isRetired) 
      return _amount * 0.9; 
     else 
      return _amount; 
    } 
} 

partisans purs POO vous diront que vous avez immédiatement besoin d'extraire un type d'employé différent et traiter chaque branche comme un sous-type différent, ce qui supprimera le « mal instruction 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; 
    } 
} 

Bien que le code soit plus facile à maintenir de cette façon, la quantité de code dans le second cas a clairement doublé. Pour une hiérarchie aussi simple, il n'y a pas besoin de refactoring à ce stade. Si vous décidez que vous avez besoin de beaucoup plus de cas spéciaux, alors votre condition peut devenir trop complexe, et vous pouvez facilement le refactoriser plus tard.

0
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"); } 
Questions connexes