Search code examples
c#delegatespredicates

Delegate Predicates Usefulness


The sample below, shows uses a predicate to evaluate a condition with hard-coded value of 100000. There is no way to add an additional parameters to the FindPoints method, for it would violate the predicate param constraint.

This brings the value of using predicates into question. Obviously Lambda solves this problem..but none the less, can anyone elaborate on the usefulness of predicates in real life scenarios, given this seemingly strange constraint.

Why would anyone use predicates if they don't accept parameters other than T?

using System;
using System.Drawing;

public class Example
{
   public static void Main()
   {
      // Create an array of Point structures.
      Point[] points = { new Point(100, 200), 
                         new Point(150, 250), new Point(250, 375), 
                         new Point(275, 395), new Point(295, 450) };

      // Define the Predicate<T> delegate.
      Predicate<Point> predicate = FindPoints;

      // Find the first Point structure for which X times Y   
      // is greater than 100000. 
      Point first = Array.Find(points, predicate);

      // Display the first structure found.
      Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y);
   }

   private static bool FindPoints(Point obj)
   {
      return obj.X * obj.Y > 100000;
   }
}
// The example displays the following output: 
//        Found: X = 275, Y = 395

EDIT: Lambda usage to do the same thing, below.

using System;
using System.Drawing;

public class Example
{
   public static void Main()
   {
      // Create an array of Point structures.
      Point[] points = { new Point(100, 200), 
                         new Point(150, 250), new Point(250, 375), 
                         new Point(275, 395), new Point(295, 450) };

      // Find the first Point structure for which X times Y   
      // is greater than 100000. 
      Point first = Array.Find(points, x => x.X * x.Y > 100000 );

      // Display the first structure found.
      Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y);
   }
}
// The example displays the following output: 
//        Found: X = 275, Y = 395

This is from MSDN. The article gives good examples, but does not seem to address my question.


Solution

  • Even when you declare your predicate using a lambda expression, providing an anonymous method instead of a named one, the lambda still only accepts a specific type. It's just that compiler infers the required type (but note that C# actually allows a variation on the lambda syntax where you do explicitly specify the type).

    So the two approaches are equally useful; if you understand why one is useful (e.g. the lambda syntax) then you understand why the other is useful.

    And they are both useful, because by passing a predicate delegate instance, you allow customization of the behavior of the method you are calling. E.g. in this case, you are using the Array.Find() method. The use of a predicate delegate instance, whether stored first in a local variable or simply passed directly, and whether declared using a named method or an anonymous one, allows the Array.Find() method to implement all the boring, repetitive work of iterating through an array, while allowing callers to provide the interesting, scenario-specific logic.

    To be clear, your two examples are really basically identical, especially since you're dealing with a static method for the predicate. There are minor differences in the declarations, but once the optimizing JIT compiler is done with the code, what's executed at runtime is practically the same. Even the IL compiled from your C# program is going to be structurally the same, though there will be some naming differences. You can verify this yourself by comparing the compiled versions, e.g. with the ildasm.exe tool or something like the dotPeek decompiler.

    I will also point out that you provided two examples – the use of a local variable and a named method, and the use of an anonymous method without a local variable – but there are at least two other examples that are also similarly identical:

    local variable with anonymous method:

      Predicate<Point> predicate = x => x.X * x.Y > 100000;
    
      Point first = Array.Find(points, predicate);
    

    named method without local variable:

      Point first = Array.Find(points, FindPoints);
    

    In that last example, the compiler infers the delegate type, creating the delegate instance automatically, much as it does when you assign the method group name to the local variable.