In C# exist two types of Lambda Expression:
A lambda expression is an anonymous function that you can use to create delegates or expression tree types.
The fist type of lambda expression is synctatic sugar for an anonymous function:
Func<int, int> myFunc = x => x + 1;
is totally equivalent to:
Func<int, int> myFunc = delegate(int x) { return x + 1; };
so it is clearly type safe, because it is C# code with a different makeup.
The other type of Lambda Expression is the one that generates Expression Trees:
Expression<Func<int, int>> myFunc = x => x + 1;
This is something different. This isn't compiled to "code" but is compiled to some object of type Expression
that "describe" the x => x + 1
(and even describe the type of delegate)... it is compiled to:
ParameterExpression par = Expression.Parameter(typeof(int), "x");
Expression<Func<int, int>> myFunc2 = Expression.Lambda<Func<int, int>>(
Expression.Add(par, Expression.Constant(1)),
par);
Now, this code can't be executed directly. It can be converted to executable code through the .Compile()
method. In general a .Compile()
d expression tree is type safe, but Expression Trees aren't normally made to be simply compiled. Programs tend to manipulate them to obtain funny result. They can be used for various tasks... For example to extract the "name" of properties or "methods" without including in the code a string with the name of the property or method, or to be converted to other languages (Entity Framework/LinqToSQL convert expression trees to SQL). An Expression Tree is quite safe (it is possible to "manually build" at runtime an invalid expression, but when you do the .Compile()
you'll get an exception, and expression trees accepted by the C# compiler are normally safe to be compiled), but if the expression tree is used for other things, then errors could occur, even errors connected to type safety.
I'll quote from: Why the anonymous type instance cannot accept null values returned by the entity framework query?
var l = (from s in db.Samples
let action = db.Actions.Where(x => s.SampleID == x.SampleID && x.ActionTypeID == 1).FirstOrDefault()
where s.SampleID == sampleID
select new
{
SampleID = s.SampleID,
SampleDate = action.ActionDate,
}).ToList();
Equivalent more or less to
var l = db.Samples.Select(s => new
{
s = s,
action = db.Actions.Where(x => s.SampleID == x.SampleID && x.ActionTypeID == 1).FirstOrDefault()
}).Where(x => x.s.SampleID == sampleID).Select(x => new
{
SampleID = x.s.SampleID,
SampleDate = x.action.ActionDate
}).ToList();
Here ActionDate
is DateTime
, and so is SampleDate
. This LINQ expression will be transformed by the compiler to a big Lambda Expression, and executed by Entity Framework SQL Server-side. Now... the problem is that action
could become null
, and so action.ActionDate
could be null
(because the expression won't be executed locally there won't be a NullReferenceException
), and an exception could be thrown (will be thrown) when null
is put in SampleDate
(an InvalidCastException
I think). So while the expression is type-safe, what the library does with it causes the expression to generate non-type-safe code (an invalid cast)