我正在处理LINQ to XML查询,并使用过匿名函数和lambda表达式。一个简单的例子是IEnumerables上的select方法。
我理解LINQ查询是延迟执行,这有点类似于懒惰评估的概念,但是当VS2012的快速监视无法处理带有lambda表达式的语句时,会想到这个问题。
在C#中,Lambda表达式是否类型安全?
我无法找到对此的直接答案,或者可能是因为我不完全理解类型安全性。我知道OCaml和Java是类型安全的并且Python是弱类型的,我能想到的另一种方式是如果语言是类型安全的,那么该语言中的lambda表达式并不特别。 There is ambiguity in strong/weak typing但是在这里我引用它,好像具有错误类型的lambda表达式将通过编译器并允许在运行时执行。如果存在抛出异常的错误,它们只会在运行时被捕获吗?
什么时候检查?编译时或运行时
例如,在编译时检查OCaml类型,并且在解析类型之前不会执行。虽然Python不那么严格,而且是一种动态语言,即使在类型错误的情况下它也会编译和执行,只在运行时捕获错误。在这个意义上,C#如何处理lambda表达式?
在提出这个问题之前,我做过一些相关的研究:
答案 0 :(得分:3)
在C#中存在两种Lambda Expression:
lambda表达式是一个匿名函数,可用于创建委托或表达式树类型。
第一种类型的lambda表达式是匿名函数的同步糖:
Func<int, int> myFunc = x => x + 1;
完全等同于:
Func<int, int> myFunc = delegate(int x) { return x + 1; };
因此它显然是类型安全的,因为它是具有不同构成的C#代码。
另一种类型的Lambda Expression是生成表达式树的那个:
Expression<Func<int, int>> myFunc = x => x + 1;
这是不同的。这不是编译为“代码”,而是编译为某种类型Expression
的对象,它“描述”x => x + 1
(甚至描述委托的类型)......它被编译为:< / p>
ParameterExpression par = Expression.Parameter(typeof(int), "x");
Expression<Func<int, int>> myFunc2 = Expression.Lambda<Func<int, int>>(
Expression.Add(par, Expression.Constant(1)),
par);
现在,此代码无法直接执行。它可以通过.Compile()
方法转换为可执行代码。通常,.Compile()
d表达式树是类型安全的,但表达式树通常不能简单地编译。程序倾向于操纵它们以获得有趣的结果。它们可用于各种任务...例如,提取属性或“方法”的“名称”,而不在代码中包含具有属性或方法名称的字符串,或转换为其他语言(实体框架) / LinqToSQL将表达式树转换为SQL)。表达式树是非常安全的(可以在运行时“手动构建”无效表达式,但是当您执行.Compile()
时,您将获得异常,并且C#编译器接受的表达式树通常是安全的。编译),但如果表达式树用于其他事情,则可能发生错误,甚至连接到类型安全的错误。
我引用: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();
或多或少等同于
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();
此处ActionDate
为DateTime
,SampleDate
也是action
。此LINQ表达式将由编译器转换为大型Lambda表达式,并由Entity Framework SQL Server端执行。现在......问题是null
可能会成为action.ActionDate
,因此null
可能是NullReferenceException
(因为表达式不会在本地执行,所以不会一个null
),当SampleDate
放入InvalidCastException
时,可能会抛出(将被抛出)异常(我认为是{{1}})。因此,虽然表达式是类型安全的,但库对它的作用会导致表达式生成非类型安全的代码(无效的转换)
答案 1 :(得分:2)
Lambda具有与任何其他C#代码一样多的静态类型检查。它构建在相同类型的系统上,并强制执行所有相同的编译时类型检查。您当然可以在lambda中关闭静态类型检查(例如,执行转换),就像在任何其他C#代码中一样。
如果将lambda编译成可执行代码(而不是Expression
)并运行,则将执行完全相同的运行时检查,就好像您没有使用lambada一样。
事实上,如果您正在使用编译成可执行代码的lambdas,它将简单地转换为新的命名方法,即使它在原始代码中是匿名的,也可以在编译器的早期版本之一中进行。一旦转换为常规命名方法,它将通过所有相同类型检查任何其他代码。
答案 2 :(得分:1)
想象一下,你可以编写一个类似这样的类:
public class Foo {
public Baz DoSomething(Bar b)
{
return new Baz(b);
}
}
显然,这在编译时是强类型的。所以现在我可以做一个像这样的委托声明:
public delegate Baz SomeDelegate(Bar b);
然后我可以修改Foo
并添加一个属性:
...
public SomeDelegate MyCall { get { return DoSomething; } }
...
你需要问问自己这样做有什么不同:
Bar b = new Bar();
Foo aFoo = new Foo();
var myDelegate = aFoo.MyCall;
Baz baz = myDelegate(b);
和
Bar b = new Bar();
var myDelegate = (Bar bar) => new Baz(bar);
Baz baz = myDelegate(b);
因为引擎盖下发生的事情非常接近于此。 lambda表达式可以通过创建一个带有方法的匿名类来实现。 (FWIW,在Java中有lambda表达式之前,我经常使用静态私有内部类来模拟它们)。从语义上讲,它比这更复杂,因为变量是自由/绑定的,以及如何优雅地处理那个混乱(提示:Java不能处理它),但最终,C#中的lambda表达式是语法糖给你一个内联定义的委托,而没有像C#那样可以处理的类型推断,并且代理是强类型的。