2009-01-01 99 views
17

我设计,做我的某些对象的延迟加载一个辅助方法,调用它看起来像这样:什么是'不可验证的代码',为什么它不好?

public override EDC2_ORM.Customer Customer { 
    get { return LazyLoader.Get<EDC2_ORM.Customer>(
      CustomerId, _customerDao,()=>base.Customer, (x)=>Customer = x); } 
    set { base.Customer = value; } 
} 

当我编译这个代码,我得到以下警告:

警告5通过来自 的匿名方法,lambda表达式, 查询表达式或迭代器结果 中的'base'关键字以不可验证的代码访问成员 'EDC2_ORM.Billing.Contract.Site' 。考虑将 访问转换为 包含类型的帮助方法。

这里的抱怨究竟是什么,为什么我做得不好?

回答

22

虚拟方法的“base.Foo”将对方法“Foo”的父定义进行非虚拟调用。从CLR 2.0开始,CLR决定在虚拟方法上进行非虚拟呼叫可能成为潜在的安全漏洞,并限制了可以使用的场景。他们将其限制在对同一类层次结构内的虚拟方法进行非虚拟调用。

Lambda表达式在过程中扭曲了一个问题。 Lambda表达式通常在一个完全独立的类中生成一个闭包。所以代码“base.Foo”最终将成为一个全新的类的表达。这会导致CLR验证异常。因此C#发出警告。

侧注:等效代码将在VB中工作。在用于虚拟方法的非虚拟调用的VB中,方法存根会在原始类中生成。非虚拟呼叫将以此方法执行。 “base.Foo”将被重定向到“StubBaseFoo”(生成的名称不同)。

2

复制/粘贴从here

Codesta:C#/ CLR有2种代码,安全和不安全。它试图提供什么以及它如何影响虚拟机? Peter Hallam:对于C#而言,这些术语是安全和不安全的。 CLR使用可验证和不可验证的条款。

当运行可验证的代码时,CLR可以执行安全策略; CLR可以阻止可验证的代码执行它没有权限执行的操作。当运行潜在的恶意代码时,例如从互联网上下载的代码,CLR将只运行可验证的代码,并确保不受信任的代码不访问任何没有权限访问的代码。

使用标准C风格指针创建无法验证的代码。 CLR本地支持C风格指针。一旦获得了C风格指针,您就可以读取或写入进程中的任何内存字节,因此运行时无法执行安全策略。其实它可能只是表现的惩罚会让它变得不切实际。

现在,这并不能完全解答您的问题(即这是为什么现在无法验证的代码),但至少它解释说,“无法证实”是CLR长期为“不安全”。我假设匿名方法和基类会在内部产生一些时髦的指针魔术。

顺便说一句:我认为代码片段不符合警告消息。代码是在谈论一个客户,这个警告是关于账单的。是否可以发布产生警告的激励码?也许你在代码中有其他的东西可以更好地解释你为什么会得到警告。

+0

假设我猜对了,它并没有真正做出任何时髦的指针魔术 - 但它使用了一个非虚拟调用给虚拟成员,我怀疑你只能从派生类型中做(可验证的) 。 – 2009-01-01 20:02:33

+0

很酷。在这个问题上学到了三件新东西 – 2009-01-01 20:06:55

+0

@Jon,这是正确的。这是一个...... CLR在2.0中添加的一个有趣的限制 – JaredPar 2009-01-01 20:07:19

9

我怀疑问题在于你基本上在说“我不想使用客户的最衍生实现 - 我想用这个特定的一个” - 你不能够正常工作。你可以在派生类中使用它,出于好的理由,但是你会违反封装的其他类型。

现在,当您使用匿名方法,lambda表达式,查询表达式(基本上使用lambda表达式)或迭代器块时,编译器有时会在幕后为您创建一个新类。有时,它可以避免为lambda表达式创建一个相同类型的新方法,但它取决于上下文。基本上,如果在lambda表达式中捕获任何局部变量,那需要一个新的类(或者实际上有多个类,这取决于范围 - 它可能会变得讨厌)。如果lambda表达式仅捕获this引用,则可以为lambda表达式逻辑创建一个新的实例方法。如果没有捕获任何东西,静态方法是好的。因此,虽然C#编译器知道你确实没有违反封装,但是CLR不会 - 所以它会以怀疑的方式对待代码。如果你是在完全信任的情况下运行,那可能不是问题,但是在其他信任级别下(我不知道这些细节),你的代码将不被允许运行。

这有帮助吗?