测试环境:
Windows 7 中文版
Visual Studio 2010 英文版 10.0.31118.1 SPRel
.net 4.0.30319 sp1Rel 英文版
BUG描述:
在一个循环中,如果循环内部创建了一个实例级的匿名委托,并让此委托持有一个实例,最后将此委托放入数组。
那么运行时的效果是:数组中的所有委托均指向同一个实例。
论证:
建立控制台应用程序,.net版本为4.0。
1
using
System;
2 using System.ComponentModel;
3
4 namespace LambdaTest
5 {
6 class Program
7 {
8 static void Main( string [] args)
9 {
10 var temp = GetConverter();
11
12 }
13
14 public static Func < object , object > [] GetConverter()
15 {
16 Func < object , object > [] converters = new Func < object , object > [ 5 ];
17 Property p;
18 Func < object , object > func;
19
20 for ( int i = 0 ; i < 5 ; i ++ )
21 {
22 p = new Property() { Name = " P " + i.ToString() };
23 func = (v) => p.Converter.ConvertTo(v, typeof (Int64));
24 converters[i] = func;
25 }
26
27 return converters;
28 }
29 }
30
31 class Property
32 {
33 public Property()
34 {
35 // test
36 this .Converter = new Int64Converter();
37 }
38 public string Name;
39
40 public virtual TypeConverter Converter { get ; private set ; }
41 }
42 }
2 using System.ComponentModel;
3
4 namespace LambdaTest
5 {
6 class Program
7 {
8 static void Main( string [] args)
9 {
10 var temp = GetConverter();
11
12 }
13
14 public static Func < object , object > [] GetConverter()
15 {
16 Func < object , object > [] converters = new Func < object , object > [ 5 ];
17 Property p;
18 Func < object , object > func;
19
20 for ( int i = 0 ; i < 5 ; i ++ )
21 {
22 p = new Property() { Name = " P " + i.ToString() };
23 func = (v) => p.Converter.ConvertTo(v, typeof (Int64));
24 converters[i] = func;
25 }
26
27 return converters;
28 }
29 }
30
31 class Property
32 {
33 public Property()
34 {
35 // test
36 this .Converter = new Int64Converter();
37 }
38 public string Name;
39
40 public virtual TypeConverter Converter { get ; private set ; }
41 }
42 }
在第25行设置断点,会发现,当i = 0时,其委托的目标对象的Name = "P0",但当i=1时,你会发现数组中,位置0的对象其目标属性Name 变更为Name = "P1",未达到我们预想的,每个委托挂接不同的实例。
分析:
通过反编译工具查看,(调整Reflector的选项,设置优化为 none)。
1
public
static
unsafe
Func
<
object
,
object
>
[] GetConverter()
2 {
3 Func < object , object > [] converters;
4 Func < object , object > func;
5 int i;
6 Property <> g__initLocal0;
7 Func < object , object > CS$ <> 9__CachedAnonymousMethodDelegate2;
8 <> c__DisplayClass3 CS$ <> 8__locals4;
9 CS$ <> 9__CachedAnonymousMethodDelegate2 = null ;
10 CS$ <> 8__locals4 = new <> c__DisplayClass3();
11 converters = new Func < object , object > [ 5 ];
12 i = 0 ;
13 goto Label_0058;
14 Label_0015:
15 <> g__initLocal0 = new Property();
16 <> g__initLocal0.Name = " P " + & i.ToString();
17 CS$ <> 8__locals4.p = <> g__initLocal0;
18 if (CS$ <> 9__CachedAnonymousMethodDelegate2 != null )
19 {
20 goto Label_004D;
21 }
22 CS$ <> 9__CachedAnonymousMethodDelegate2 = new Func < object , object > (CS$ <> 8__locals4. < GetConverter > b__1);
23 Label_004D:
24 func = CS$ <> 9__CachedAnonymousMethodDelegate2;
25 converters[i] = func;
26 i += 1 ;
27 Label_0058:
28 if (i < 5 )
29 {
30 goto Label_0015;
31 }
32 return converters;
33 }
2 {
3 Func < object , object > [] converters;
4 Func < object , object > func;
5 int i;
6 Property <> g__initLocal0;
7 Func < object , object > CS$ <> 9__CachedAnonymousMethodDelegate2;
8 <> c__DisplayClass3 CS$ <> 8__locals4;
9 CS$ <> 9__CachedAnonymousMethodDelegate2 = null ;
10 CS$ <> 8__locals4 = new <> c__DisplayClass3();
11 converters = new Func < object , object > [ 5 ];
12 i = 0 ;
13 goto Label_0058;
14 Label_0015:
15 <> g__initLocal0 = new Property();
16 <> g__initLocal0.Name = " P " + & i.ToString();
17 CS$ <> 8__locals4.p = <> g__initLocal0;
18 if (CS$ <> 9__CachedAnonymousMethodDelegate2 != null )
19 {
20 goto Label_004D;
21 }
22 CS$ <> 9__CachedAnonymousMethodDelegate2 = new Func < object , object > (CS$ <> 8__locals4. < GetConverter > b__1);
23 Label_004D:
24 func = CS$ <> 9__CachedAnonymousMethodDelegate2;
25 converters[i] = func;
26 i += 1 ;
27 Label_0058:
28 if (i < 5 )
29 {
30 goto Label_0015;
31 }
32 return converters;
33 }
我发现编译器错误的进行了优化,仅在第一次创建委托实例(第18行),而此委托持有的实例p(第17行),在循环中不断改变。
我记忆中,C#的编译器在处理静态方法时,有时候的确会作此优化,之前也看过其他的反编译,当时还觉得编译器真聪明。根据此观点,我将函数改为实例级的,还是不行。
继续,通过将程序23行修改,改为调用一个方法获取委托:
func
=
GetFunc(p)
其GetFunc(p)定义如下:
1
public
static
Func
<
object
,
object
>
GetFunc(Property p)
2 {
3 return (v) => p.Converter.ConvertTo(v, typeof (Int64));
4 }
2 {
3 return (v) => p.Converter.ConvertTo(v, typeof (Int64));
4 }