locked
Understanding Lambda Expression RRS feed

  • Question

  • Hi,

    I would like to kindly ask someone with more experience to help me understand the code below. If it is possible to write the same code using another way, I would appreciate it very much if you could provice some exple code.

    Cheers

    1 - I know the code below is using a lambda expressions to call a method but I cannot figure out how to interpret it.
     
    public void GetCustomers(
                Action<IEnumerable<Customer>, Exception> callback)
            {

     
    This method expects two parameters, right?  - a List of Customer and an Exception?
     
    But when we call this method from code behing, we are not initializing the parameters we are passing to any value or giving them any types.

    So how does it know what type it should be and where is the values assigned to them? 

    2 -  Here is where the real WCF service is called:
     
    client.GetAllCustomersCompleted += (s, e) =>
                {

    But are we calling a method or is this an event handler?
     
    Also what does the (s,e) mean
     
    Why we call the service only at the end of the method?
     
    client.GetAllCustomersAsync(callback);
     
    What does the callback mean? What will it contain?

    CODE

    public class DataService
        {
            public void GetCustomers(
                Action<IEnumerable<Customer>, Exception> callback)
            {
                var client = new CustomerServiceClient("BasicEndPoint");

                client.GetAllCustomersCompleted += (s, e) =>
                {
                    var userCallback = e.UserState
                        as Action<IEnumerable<Customer>, Exception>;

                    if (userCallback == null)
                    {
                        return;
                    }

                    if (e.Error != null)
                    {
                        userCallback(null, e.Error);
                        return;
                    }

                    userCallback(e.Result, null);
                };

                client.GetAllCustomersAsync(callback);
            }
        }

     

    var service = new DataService();

                service.GetCustomers((customers, error) =>
                {
                    if (error != null)
                    {
                        MessageBox.Show(error.Message);
                    }
                    else
                    {

                        // Error in this call
                        Customers = new ObservableCollection<Customer>(customers);
                    }
                });

     

    Tuesday, April 13, 2010 5:08 PM

Answers

  • OK, first, let's try to explain the async-get pattern. Basically, to avoid block on waiting network response, you can retrieve the data asynchronously. That means you issue a "Begin operation" call, the network interface initializes and starts doing a bunch of stuff, but the call will return immediately regardless of the outcome. So in order to get the results, the "Begin" call lets you pass in a function which will get called once the operation is completed. This function is named a "callback" function. 

    C# handles passing address of functions with delegates. So for example, if the callback function wants two parameters, IEnumerable<Customers> and Exception, then you can define the delegate as:

    Action<IEnumerable<Customer>, Exception>

    if you look at the definition of "Action", you'll see this is

    void delegate Action(IEnumerable<Customer> obj1, Exception obj2);

    To use this, you would write:

    Action<IEnumerable<Customer>, Exception> callback = MyCallBackFunction;
    
    ...
    
    private void MyCallBackFunction(IEnumerable<Customer> customers, Exception ex)
    {
       // actual call back function
    }

     

    This is, of course, very inconvenient since you have to start another body of code somewhere else. The logic presentation does not flow well either. So in this case you can use anonymous functions, and put "MyCallBackFunction" together with the definition of callback:

    Action<IEnumerable<Customer>, Exception> callback = delegate(IEnumerable<Customer> customers, Exception ex)
    {
       // the anonymous callback
    };

    This syntax was introduced in C# 2.0, now with lambda expressions, you can shrink the anonymous function definition even shorter, to just:

    Action<IEnumerable<Customer>, Exception> callback = (customers,ex) =>
    {
       // the anonymous callback
    };

    The compiler knows the type of customers and ex from the type of callback - it HAS TO BE IEnumerable<Customer> and Exception.

    Therefore, looking back to your code:

    in service.GetCustomers((customers, error) => { ... });

    The lambda expression (customers, error) => { } defines an anonymous method with two parameters customers and error, just like the above example. This method is passed into "GetCustomers" (note: only passed into, not called yet). In GetCustomers, this anonymous method is passed further down to

    client.GetAllCustomersAsync(callback);

    I'd imagine somewhere in the client, there is a logic saying "When this operation completes, raise the GetAllCustomersCompleted event with a state parameter". In this case, the state parameters seems to be just the callback method. (state.UserCallBack = callback);

    So, to get the result, you hook up to the event. To do that you can either define another event handler method, or you can simply use the lambda expression, which they did in this case. So (s, e) => is the function signature, I guess "s" stands for source, and "e" for eventargs. You'd have to dig up the event definition to see what the actual type of s and e are.

    So, inside the handler, when there is no error, you just invoke the callback by:

    userCallback(e.Result, null);

    and now, the anonymous method defined in "GetCustomers" ((customers, error) => {}) is finally invoked with customers = e.Results, and error = null.

    There are many resources on lambda expressions available on MSDN:

    http://msdn.microsoft.com/en-us/library/bb397687.aspx

    What you saw here is an example of statement lambda.

     


    Regards,

    PQ



    Peter Q. http://blogs.msdn.com/peter_qian
    Tuesday, April 13, 2010 6:32 PM
    Answerer

All replies

  • OK, first, let's try to explain the async-get pattern. Basically, to avoid block on waiting network response, you can retrieve the data asynchronously. That means you issue a "Begin operation" call, the network interface initializes and starts doing a bunch of stuff, but the call will return immediately regardless of the outcome. So in order to get the results, the "Begin" call lets you pass in a function which will get called once the operation is completed. This function is named a "callback" function. 

    C# handles passing address of functions with delegates. So for example, if the callback function wants two parameters, IEnumerable<Customers> and Exception, then you can define the delegate as:

    Action<IEnumerable<Customer>, Exception>

    if you look at the definition of "Action", you'll see this is

    void delegate Action(IEnumerable<Customer> obj1, Exception obj2);

    To use this, you would write:

    Action<IEnumerable<Customer>, Exception> callback = MyCallBackFunction;
    
    ...
    
    private void MyCallBackFunction(IEnumerable<Customer> customers, Exception ex)
    {
       // actual call back function
    }

     

    This is, of course, very inconvenient since you have to start another body of code somewhere else. The logic presentation does not flow well either. So in this case you can use anonymous functions, and put "MyCallBackFunction" together with the definition of callback:

    Action<IEnumerable<Customer>, Exception> callback = delegate(IEnumerable<Customer> customers, Exception ex)
    {
       // the anonymous callback
    };

    This syntax was introduced in C# 2.0, now with lambda expressions, you can shrink the anonymous function definition even shorter, to just:

    Action<IEnumerable<Customer>, Exception> callback = (customers,ex) =>
    {
       // the anonymous callback
    };

    The compiler knows the type of customers and ex from the type of callback - it HAS TO BE IEnumerable<Customer> and Exception.

    Therefore, looking back to your code:

    in service.GetCustomers((customers, error) => { ... });

    The lambda expression (customers, error) => { } defines an anonymous method with two parameters customers and error, just like the above example. This method is passed into "GetCustomers" (note: only passed into, not called yet). In GetCustomers, this anonymous method is passed further down to

    client.GetAllCustomersAsync(callback);

    I'd imagine somewhere in the client, there is a logic saying "When this operation completes, raise the GetAllCustomersCompleted event with a state parameter". In this case, the state parameters seems to be just the callback method. (state.UserCallBack = callback);

    So, to get the result, you hook up to the event. To do that you can either define another event handler method, or you can simply use the lambda expression, which they did in this case. So (s, e) => is the function signature, I guess "s" stands for source, and "e" for eventargs. You'd have to dig up the event definition to see what the actual type of s and e are.

    So, inside the handler, when there is no error, you just invoke the callback by:

    userCallback(e.Result, null);

    and now, the anonymous method defined in "GetCustomers" ((customers, error) => {}) is finally invoked with customers = e.Results, and error = null.

    There are many resources on lambda expressions available on MSDN:

    http://msdn.microsoft.com/en-us/library/bb397687.aspx

    What you saw here is an example of statement lambda.

     


    Regards,

    PQ



    Peter Q. http://blogs.msdn.com/peter_qian
    Tuesday, April 13, 2010 6:32 PM
    Answerer
  • Hi Peter,

    Many thanks for your reply. I have just seen it.

    I will try to digest all that and if I still have not understood one of your points I will let you know.

    Again I really appreciated your help.

    Cheers

    C

     

     

     

    Tuesday, April 13, 2010 7:50 PM
  • Hi Peter,

    Your explanation was very clear and it helped me a lot to step up the right way.

    You said:

    "I'd imagine somewhere in the client, there is a logic saying "When this operation completes, raise the GetAllCustomersCompleted event with a state parameter". "

    If I understood your explanation about events then I think that the code below is where the even handler is set up. This code is inside a head service on top of the WCF real service.

    Please could comment on that?

    client.GetAllCustomersCompleted += (s, e) =>
    {
           var client = new CustomerServiceClient("BasicEndPoint");

           var userCallback = e.UserState
           as Action<IEnumerable<Customer>, Exception>; 

     

    Also, unfortunately the code snippet I saw did not show the implementation of the WCF Service to get the list of Customers.

    I was wondering if you have any ideas on how I could create a WCF service so that I could debug from the client all the way to the WCF service and see how this all works.

    Would it be hard to produce a simple mock up service that return a hard coded list of customers so that I can hook it up to the code above? If not could you please give me some hints on how to achive that?

    Cheers

    C

     

     

    Tuesday, April 13, 2010 8:11 PM
  • It looks like the code is on the client side, rather on the server side. I should point out that your question is actually related to WCF rather than "WCF Data Service". They should be posted here: http://social.msdn.microsoft.com/Forums/en-US/wcf/threads

    However, if you are looking for a simple solution to expose restful data on the web, I think you should give WCF Data Service a try.

    Our data developer center: http://msdn.microsoft.com/en-us/data/bb931106.aspx

    and the protocol we speak: http://www.odata.org/

     

    Regards,

    PQ


    Peter Q. http://blogs.msdn.com/peter_qian
    Tuesday, April 13, 2010 10:32 PM
    Answerer
  • Thanks
    Thursday, April 15, 2010 9:16 PM