简体   繁体   中英

Methods that return a delegate

I'm trying hard to make sense of delegates and lambda expressions and were reading through some of the questions here on stackoverflow and landed on this post where the second example in this comment got me completely lost. The first thing that confuses me is the list parameter not defined anywhere (I understand that it is the input parameter of the return type yet I find difficult to make sense of this code) but I think that everything would be much more clear by seeing how such a definition could be used in practice (and this is the second thing I'm having a hard time grasping).

How could methods such as the following be used in practice?

public Func<IList<T>, T> SelectionMethod<T>()
{
    return list => list.First();
}

public Func<float, float> QuadraticFunctionMaker(float a , float b , float c)
{
    return (x) => { return a * x * x  + b * x + c; };
}

The parameters are defined, it's just that their types are inferred. The argument definition is the part before the => - list (inferred to be of type IList<T> ) and x (inferred to be of type float ) respectivelly in your examples.

The first delegate corresponds to a signature of:

T SomeMethod<T>(IList<T> list)

The second is:

float SomeMethod(float x)

Since the compiler knows what the signature of the delegate must be, it can infer the required types automatically. If you were to write out the delegate using the old-school explicit syntax, it would look something like this:

return (Func<IList<T>, T>)(delegate (IList<T> list) { return list.First(); });

If you really want to use explicit typing, you can specify the types as needed:

(IList<T> list) => list.First()

When you actually want to invoke the delegate, you need to pass the argument, eg:

SelectionMethod<string>()(new List<string>())

The first lambda expression is very simple. The second additionally closes over the arguments to the "creator" function, which means that you can access the arguments to the "creator" in the body of the delegate. This is entirely safe in purely immutable code, but may be tricky if you're dealing with mutable reference types and side-effects - make sure you understand the semantics properly before you do anything crazy with those.

Depending on your experience with functional programming, it might be helpful to realize that all of this is just compiler trickery. The two methods will compile to the equivalent of something like this:

public Func<IList<T>, T> SelectionMethod<T>()
{
  return new Func<IList<T>, T>(__HiddenAnonymousMethod);
}

private T __HiddenAnonymousMethod<T>(IList<T> list)
{
  return list.First();
}

The second example is more complicated because of the closure - we need to create a "helper object" to hold the captured locals:

private class __HiddenAnonymousClass
{
  float a, b, c;

  public __HiddenAnonymousClass(float a, float b, float c)
  {
    this.a = a; this.b = b; this.c = c;
  }

  public float __HiddenAnonymousMethod(float x)
  {
    return a * x * x  + b * x + c;
  }
}

public Func<float, float> QuadraticFunctionMaker(float a , float b , float c)
{
  return new Func<float, float>
    (new __HiddenAnonymousClass(a, b, c).__HiddenAnonymousMethod);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM