X# contains 3 language constructs that are fairly similar yet different.
Historically the XBase language has known the CodeBlock Type. A codeblock is specified as
{| params | expression }
The parameters inside a codeblock are of type USUAL and its return value is also of type USUAL.
To evaluate a codeblock you call the Eval() runtime function and pass this function the codeblock and parameters when needed
FUNCTION Start() AS VOID
LOCAL cb as CODEBLOCK
cb := { |x, y| x * y}
? Eval(cb, 2,3) // shows 6
WAIT
RETURN
In stead of a single expression, you can also use an expression list. The value of the last expression in the list is returned as the result of the codeblock.
FUNCTION Start() AS VOID
LOCAL cb as CodeBlock
cb := { |x, y| x += 1, x * y}
? Eval(cb, 2,3) // shows 9
WAIT
RETURN
X# has also added the possibility to the language to include a list of statements as "body" of the codeblock. The result of the last statement is returned to the calling code. When the last statement is "Void" then a NIL will be returned:
Note that the closing Curly must be on a line of its own and the first statement must appear on a new line as well.
FUNCTION Start() AS VOID
LOCAL cb as CodeBlock
cb := { |x, y|
x += 1
? x
RETURN x * y
}
? Eval(cb, 2,3) // prints 3 and shows the result 9
WAIT
RETURN
Lamda expressions look a lot like Codeblocks. They are usually used in combination with Delegates.
DELEGATE MultiPlyInt( x as Int, y as Int) as Int
FUNCTION Start() AS VOID
LOCAL lambda as MultiPlyInt
lambda := { x, y => x * y}
? Lambda(2,3) // shows 6
RETURN
Parameters are optional and the return type can be VOID, so this works as well
DELEGATE DoSomething( ) as VOID
FUNCTION Start() AS VOID
LOCAL lambda as DoSomething
lambda := { => Console.WriteLine("This is a Lambda")}
Lambda() // prints the text
RETURN
The parameters of a Lambda expression may be typed. This can be convenient for documentation purposes but can also help the compiler to find the right overload for a method:
DELEGATE CalcInt( x AS INT, y AS INT) AS INT
DELEGATE CalcReal( x AS REAL8, y AS REAL8) AS REAL8
FUNCTION Start() AS VOID
TestLambda( { x AS INT, y AS INT => x * y} )
TestLambda( { x AS REAL8, y AS REAL8 => x + y} )
TestLambda( { x , y => x - y } ) // Which one will be called ?
RETURN
FUNCTION TestLambda (lambda AS CalcInt) AS VOID
? "Int", lambda(2,3)
RETURN
FUNCTION TestLambda (lambda AS CalcReal) AS VOID
? "Real",lambda(2,3)
RETURN
The body of the Lambda may also be a single expression, expression list and a statement list.
Anonymous Method Expressions
These work almost the same as Lambda Expressions.
Take the example below:
FUNCTION TestAnonymous() AS VOID
LOCAL oForm AS Form
oForm := Form{}
oForm:Click += DELEGATE(o AS System.Object, e AS System.EventArgs ) {
System.Windows.Forms.MessageBox.Show("Click from AME 1!")
System.Windows.Forms.MessageBox.Show("Click from AME 2!")
}
oForm:Click += { o,e => System.Windows.Forms.MessageBox.Show("We can also do this with a Lambda!") }
oForm:ShowDialog()
RETURN
The biggest difference between Lambda Expressions and Anonymous Method Expressions is that the parameters to Lambda Expressions do not have to be typed. They will be inferred from the usage. Parameters for Anonymous Method Expressions must always be typed.