Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to declare a C# lambda and immediately call it?

Tags:

c#

lambda

It's possible to declare a lambda function and immediately call it:

Func<int, int> lambda = (input) => { return 1; };
int output = lambda(0);

I'm wondering if it's possible to do so in one line, e.g. something like

int output = (input) => { return 1; }(0);

which gives a compiler error "Method name expected". Casting to Func<int, int> doesn't work either:

int output = (Func<int, int>)((input) => { return 1; })(0);

gives the same error, and for reasons mentioned below I'd like to avoid having to explicitly specify the input argument type (the first int).


You're probably wondering why I want to do this, instead of just embedding the code directly, e.g. int output = 1;. The reason is as follows: I've generated a reference for a SOAP webservice with svcutil, which because of the nested elements generates extremely long class names, which I'd like to avoid having to type out. So instead of

var o = await client.GetOrderAsync(request);
return new Order {
    OrderDate = o.OrderDate,
    ...
    Shipments = o.Shipment_Order == null ? new Shipment[0]
        o.Shipment_Order.Select(sh => new Shipment {
            ShipmentID = sh.ShipmentID,
            ...
            Address = CreateAddress(sh.ReceiverAddress_Shipment);
        }).ToArray()
};

and a separate CreateAddress(GetOrderResultOrderShipment_OrderShipmentShipment_Address address) method (real names are even longer, and I have very limited control about the form), I'd like to write

var o = await client.GetOrderAsync(request);
return new Order {
    OrderDate = o.OrderDate,
    ...
    Shipments = o.Shipment_Order == null ? new Shipment[0]
        o.Shipment_Order.Select(sh => new Shipment {
            ShipmentID = sh.ShipmentID,
            ...
            Address = sh.ReceiverAddress_Shipment == null ? null : () => {
                var a = sh.ReceiverAddress_Shipment.Address;
                return new Address {
                    Street = a.Street
                    ...
                };
            }()
        }).ToArray()
};

I know I could write

Address = sh.ReceiverAddress_Shipment == null ? null : new Address {
    Street = sh.ReceiverAddress_Shipment.Address.Street,
    ...
}

but even that (the sh.ReceiverAddress_Shipment.Address part) becomes very repetitive if there are many fields. Declaring a lambda and immediately calling it would be more elegant less characters to write.

like image 311
Glorfindel Avatar asked Jan 17 '20 10:01

Glorfindel


People also ask

How do you declare a constant in C?

The const keyword Variables can be declared as constants by using the “const” keyword before the datatype of the variable. The constant variables can be initialized once only. The default value of constant variables are zero.

What is C declaration?

A declaration is a C language construct that introduces one or more identifiers into the program and specifies their meaning and properties. Declarations may appear in any scope.

Which is valid C declaration?

In C language, a variable name can consists of letters, digits and underscore i.e. _ . But a variable name has to start with either letter or underscore. It can't start with a digit. So valid variables are var_9 and _ from the above question.


4 Answers

Instead of trying to cast the lambda, I propose you use a small helper function:

public static TOut Exec<TIn, TOut>(Func<TIn, TOut> func, TIn input) => func(input); 

which you could then use like this: int x = Exec(myVar => myVar + 2, 0);. This reads a lot nicer to me than the alternatives suggested here.

like image 106
germi Avatar answered Sep 30 '22 18:09

germi


It's ugly, but it's possible:

int output = ((Func<int, int>)(input => { return 1; }))(0); 

Anonymous functions, including lambda expressions, are implicitly convertible to a delegate that matches their signature, but this syntax requires the lambda to be enclosed in parentheses.

The above can be simplified as well:

int output = ((Func<int, int>)(input => 1))(0); 
like image 29
Johnathan Barclay Avatar answered Sep 30 '22 20:09

Johnathan Barclay


Lambda literals in C# have a curious distinction in that their meaning is dependent on their type. They are essentially overloaded on their return type which is something does not exist anywhere else in C#. (Numeric literals are somewhat similar.)

The exact same lambda literal can either evaluate to an anonymous function that you can execute (i.e. a Func/Action) or an abstract representation of the operations inside of the Body, kind of like an Abstract Syntax Tree (i.e. a LINQ Expression Tree).

The latter is, for example, how LINQ to SQL, LINQ to XML, etc. work: the lambdas do not evaluate to executable code, they evaluate to LINQ Expression Trees, and the LINQ provider can then use those Expression Trees to understand what the body of the lambda is doing and generate e.g. a SQL Query from that.

In your case, there is no way for the compiler to know wheter the lambda literal is supposed to be evaluated to a Func or a LINQ Expression. That is why Johnathan Barclay's answer works: it gives a type to the lambda expression and therefore, the compiler knows that you want a Func with compiled code that executes the body of your lambda instead of an un-evaluated LINQ Expression Tree that represents the code inside the body of the lambda.

like image 36
Jörg W Mittag Avatar answered Sep 30 '22 20:09

Jörg W Mittag


You could inline the declaration of the Func by doing

int output = (new Func<int, int>(() => { return 1; }))(0);

and immediately invoking it.

like image 34
phuzi Avatar answered Sep 30 '22 18:09

phuzi