Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
2021715c
R
roslyn
项目概览
lwm1986
/
roslyn
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
roslyn
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
2021715c
编写于
3月 17, 2018
作者:
N
Neal Gafter
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Optimize a type test followed by a cast, a common situation in pattern-matching.
Fixes #20641
上级
dceb3ba2
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
546 addition
and
262 deletion
+546
-262
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs
...lRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs
+88
-44
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs
...Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs
+56
-5
src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs
src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs
+275
-87
src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs
src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs
+72
-72
src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs
...p/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs
+37
-37
src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
+18
-17
未找到文件。
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs
浏览文件 @
2021715c
...
...
@@ -142,47 +142,114 @@ private void LowerDecisionDag(ImmutableArray<BoundDecisionDag> sortedNodes)
break
;
}
// Call LowerDecisionDagNode with each node and its following node in the generation order.
// However, some nodes can be emitted more efficiently as a switch dispatch (possibly out
// of order).
var
loweredNodes
=
PooledHashSet
<
BoundDecisionDag
>.
GetInstance
();
BoundDecisionDag
previous
=
null
;
// Code for the when clause goes in a separate code section for the switch section.
foreach
(
BoundDecisionDag
node
in
sortedNodes
)
{
if
(
node
is
BoundWhenClause
w
)
{
// Code for the when clause goes in a separate code section for the switch section.
LowerWhenClause
(
w
);
}
}
ImmutableArray
<
BoundDecisionDag
>
nodesToLower
=
sortedNodes
.
WhereAsArray
(
n
=>
n
.
Kind
!=
BoundKind
.
WhenClause
&&
n
.
Kind
!=
BoundKind
.
Decision
);
var
loweredNodes
=
PooledHashSet
<
BoundDecisionDag
>.
GetInstance
();
for
(
int
i
=
0
,
length
=
nodesToLower
.
Length
;
i
<
length
;
i
++)
{
BoundDecisionDag
node
=
nodesToLower
[
i
];
if
(
loweredNodes
.
Contains
(
node
))
{
continue
;
}
// BoundDecision nodes do not get any code generated for them.
if
(
node
is
BoundDecision
||
loweredNodes
.
Contains
(
node
))
if
(
this
.
_dagNodeLabels
.
TryGetValue
(
node
,
out
LabelSymbol
label
))
{
_loweredDecisionDag
.
Add
(
_factory
.
Label
(
label
));
}
// If we can generate an IL switch instruction, do so
if
(
GenerateSwitchDispatch
(
node
,
loweredNodes
))
{
continue
;
}
if
(
previous
!=
null
&&
!
loweredNodes
.
Contains
(
previous
))
// If we can generate a type test and cast more efficiently as an `is` followed by a null check, do so
if
(
GenerateTypeTestAndCast
(
node
,
loweredNodes
,
nodesToLower
,
i
))
{
// Lower the node "previous".
if
(!
GenerateSwitchDispatch
(
previous
,
loweredNodes
))
{
LowerDecisionDagNode
(
previous
,
node
);
}
continue
;
}
loweredNodes
.
Add
(
previous
);
// We pass the node that will follow so we can permit a test to fall through if appropriate
BoundDecisionDag
nextNode
=
((
i
+
1
)
<
length
)
?
nodesToLower
[
i
+
1
]
:
null
;
if
(
nextNode
!=
null
&&
loweredNodes
.
Contains
(
nextNode
))
{
nextNode
=
null
;
}
previous
=
node
;
LowerDecisionDagNode
(
node
,
nextNode
)
;
}
// Lower the final node
if
(
previous
!=
null
&&
!
loweredNodes
.
Contains
(
previous
))
loweredNodes
.
Free
();
}
/// <summary>
/// If we have a type decision followed by a cast to that type, and the types are reference types,
/// then we can replace the pair of them by a conversion using `as` and a null check.
/// </summary>
/// <returns>true if we generated code for the decision</returns>
private
bool
GenerateTypeTestAndCast
(
BoundDecisionDag
node
,
HashSet
<
BoundDecisionDag
>
loweredNodes
,
ImmutableArray
<
BoundDecisionDag
>
nodesToLower
,
int
indexOfNode
)
{
Debug
.
Assert
(
node
==
nodesToLower
[
indexOfNode
]);
if
(
node
is
BoundDecisionPoint
decisionPoint
&&
decisionPoint
.
WhenTrue
is
BoundEvaluationPoint
evaluationPoint
&&
// Even if there are other entries to the evaluation point, we need not use it here
// !this._dagNodeLabels.ContainsKey(evaluationPoint) &&
TryLowerTypeTestAndCast
(
decisionPoint
.
Decision
,
evaluationPoint
.
Evaluation
,
out
BoundExpression
sideEffect
,
out
BoundExpression
test
)
)
{
LowerDecisionDagNode
(
previous
,
null
);
var
whenTrue
=
evaluationPoint
.
Next
;
var
whenFalse
=
decisionPoint
.
WhenFalse
;
if
(!
this
.
_dagNodeLabels
.
ContainsKey
(
evaluationPoint
))
{
loweredNodes
.
Add
(
evaluationPoint
);
}
var
nextNode
=
(
indexOfNode
+
2
<
nodesToLower
.
Length
)
&&
nodesToLower
[
indexOfNode
+
1
]
==
evaluationPoint
&&
!
loweredNodes
.
Contains
(
nodesToLower
[
indexOfNode
+
2
])
?
nodesToLower
[
indexOfNode
+
2
]
:
null
;
_loweredDecisionDag
.
Add
(
_factory
.
ExpressionStatement
(
sideEffect
));
GenerateTest
(
test
,
whenTrue
,
whenFalse
,
nextNode
);
return
true
;
}
loweredNodes
.
Free
();
return
false
;
}
private
void
GenerateTest
(
BoundExpression
test
,
BoundDecisionDag
whenTrue
,
BoundDecisionDag
whenFalse
,
BoundDecisionDag
nextNode
)
{
// Because we have already "optimized" away tests for a constant switch expression, the decision should be nontrivial.
Debug
.
Assert
(
test
!=
null
);
if
(
nextNode
==
whenFalse
)
{
_loweredDecisionDag
.
Add
(
_factory
.
ConditionalGoto
(
test
,
GetDagNodeLabel
(
whenTrue
),
jumpIfTrue
:
true
));
// fall through to false decision
}
else
if
(
nextNode
==
whenTrue
)
{
_loweredDecisionDag
.
Add
(
_factory
.
ConditionalGoto
(
test
,
GetDagNodeLabel
(
whenFalse
),
jumpIfTrue
:
false
));
// fall through to true decision
}
else
{
_loweredDecisionDag
.
Add
(
_factory
.
ConditionalGoto
(
test
,
GetDagNodeLabel
(
whenTrue
),
jumpIfTrue
:
true
));
_loweredDecisionDag
.
Add
(
_factory
.
Goto
(
GetDagNodeLabel
(
whenFalse
)));
}
}
/// <summary>
...
...
@@ -358,11 +425,6 @@ private void LowerWhenClause(BoundWhenClause whenClause)
/// </summary>
private
void
LowerDecisionDagNode
(
BoundDecisionDag
node
,
BoundDecisionDag
nextNode
)
{
if
(
this
.
_dagNodeLabels
.
TryGetValue
(
node
,
out
LabelSymbol
label
))
{
_loweredDecisionDag
.
Add
(
_factory
.
Label
(
label
));
}
switch
(
node
)
{
case
BoundEvaluationPoint
evaluationPoint
:
...
...
@@ -383,25 +445,7 @@ private void LowerDecisionDagNode(BoundDecisionDag node, BoundDecisionDag nextNo
{
// PROTOTYPE(patterns2): should translate a chain of constant value tests into a switch instruction as before
BoundExpression
test
=
base
.
LowerDecision
(
decisionPoint
.
Decision
);
// Because we have already "optimized" away tests for a constant switch expression, the decision should be nontrivial.
Debug
.
Assert
(
test
!=
null
);
if
(
nextNode
==
decisionPoint
.
WhenFalse
)
{
_loweredDecisionDag
.
Add
(
_factory
.
ConditionalGoto
(
test
,
GetDagNodeLabel
(
decisionPoint
.
WhenTrue
),
jumpIfTrue
:
true
));
// fall through to false decision
}
else
if
(
nextNode
==
decisionPoint
.
WhenTrue
)
{
_loweredDecisionDag
.
Add
(
_factory
.
ConditionalGoto
(
test
,
GetDagNodeLabel
(
decisionPoint
.
WhenFalse
),
jumpIfTrue
:
false
));
// fall through to true decision
}
else
{
_loweredDecisionDag
.
Add
(
_factory
.
ConditionalGoto
(
test
,
GetDagNodeLabel
(
decisionPoint
.
WhenTrue
),
jumpIfTrue
:
true
));
_loweredDecisionDag
.
Add
(
_factory
.
Goto
(
GetDagNodeLabel
(
decisionPoint
.
WhenFalse
)));
}
GenerateTest
(
test
,
decisionPoint
.
WhenTrue
,
decisionPoint
.
WhenFalse
,
nextNode
);
}
break
;
...
...
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs
浏览文件 @
2021715c
...
...
@@ -255,6 +255,42 @@ protected BoundExpression LowerDecision(BoundDagDecision decision)
throw
ExceptionUtilities
.
UnexpectedValue
(
decision
);
}
}
/// <summary>
/// Lower a decision followed by an evaluation into a side-effect followed by a test. This permits us to optimize
/// a type test followed by a cast into an `as` expression followed by a null check. Returns true if the optimization
/// applies and the results are placed into <paramref name="sideEffect"/> and <paramref name="test"/>. The caller
/// should place the side-effect before the test in the generated code.
/// </summary>
/// <param name="evaluation"></param>
/// <param name="decision"></param>
/// <param name="sideEffect"></param>
/// <param name="test"></param>
/// <returns>true if the optimization is applied</returns>
protected
bool
TryLowerTypeTestAndCast
(
BoundDagDecision
decision
,
BoundDagEvaluation
evaluation
,
out
BoundExpression
sideEffect
,
out
BoundExpression
test
)
{
if
(
decision
is
BoundTypeDecision
typeDecision
&&
evaluation
is
BoundDagTypeEvaluation
typeEvaluation
&&
typeDecision
.
Type
.
IsReferenceType
&&
typeDecision
.
Input
.
Type
.
IsReferenceType
&&
typeEvaluation
.
Type
==
typeDecision
.
Type
&&
typeEvaluation
.
Input
==
typeDecision
.
Input
)
{
BoundExpression
input
=
_tempAllocator
.
GetTemp
(
decision
.
Input
);
BoundExpression
output
=
_tempAllocator
.
GetTemp
(
new
BoundDagTemp
(
evaluation
.
Syntax
,
typeEvaluation
.
Type
,
evaluation
,
0
));
sideEffect
=
_factory
.
AssignmentExpression
(
output
,
_factory
.
As
(
input
,
typeEvaluation
.
Type
));
test
=
_factory
.
ObjectNotEqual
(
output
,
_factory
.
Null
(
output
.
Type
));
return
true
;
}
sideEffect
=
test
=
null
;
return
false
;
}
}
private
class
IsPatternExpressionLocalRewriter
:
PatternLocalRewriter
...
...
@@ -348,18 +384,32 @@ public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation co
}
// We follow the "good" path in the decision dag. We depend on it being nicely linear in structure.
// If we add "or" patterns that assumption breaks down.
while
(
dag
.
Kind
!=
BoundKind
.
Decision
&&
dag
.
Kind
!=
BoundKind
.
WhenClause
)
{
switch
(
dag
)
{
case
BoundEvaluationPoint
e
:
LowerOneDecision
(
e
.
Evaluation
);
dag
=
e
.
Next
;
{
LowerOneDecision
(
e
.
Evaluation
);
dag
=
e
.
Next
;
}
break
;
case
BoundDecisionPoint
d
:
LowerOneDecision
(
d
.
Decision
);
Debug
.
Assert
(
d
.
WhenFalse
is
BoundDecision
x
&&
x
.
Label
==
failureLabel
);
dag
=
d
.
WhenTrue
;
{
Debug
.
Assert
(
d
.
WhenFalse
is
BoundDecision
x
&&
x
.
Label
==
failureLabel
);
if
(
d
.
WhenTrue
is
BoundEvaluationPoint
e
&&
TryLowerTypeTestAndCast
(
d
.
Decision
,
e
.
Evaluation
,
out
BoundExpression
sideEffect
,
out
BoundExpression
test
))
{
_sideEffectBuilder
.
Add
(
sideEffect
);
AddConjunct
(
test
);
dag
=
e
.
Next
;
}
else
{
LowerOneDecision
(
d
.
Decision
);
dag
=
d
.
WhenTrue
;
}
}
break
;
}
}
...
...
@@ -422,6 +472,7 @@ public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation co
}
}
private
BoundExpression
MakeEqual
(
BoundExpression
loweredLiteral
,
BoundExpression
input
)
{
Debug
.
Assert
(
loweredLiteral
.
Type
==
input
.
Type
);
...
...
src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs
浏览文件 @
2021715c
...
...
@@ -345,14 +345,14 @@ static void Main(string[] args)
var
compVerifier
=
CompileAndVerify
(
compilation
,
expectedOutput
:
expectedOutput
);
compVerifier
.
VerifyIL
(
"Program.Main"
,
@"{
// Code size
106 (0x6a
)
// Code size
96 (0x60
)
.maxstack 2
.locals init (Base<object> V_0, //x
Derived V_1, //y
Derived V_2,
Derived V_3, //z
Base<object>
V_4,
Derived
V_5,
Derived
V_4,
Base<object>
V_5,
Base<object> V_6)
IL_0000: nop
IL_0001: newobj ""Derived..ctor()""
...
...
@@ -365,42 +365,40 @@ .maxstack 2
IL_0015: nop
IL_0016: ldloc.0
IL_0017: isinst ""Derived""
IL_001c: brfalse.s IL_002a
IL_001e: ldloc.0
IL_001f: castclass ""Derived""
IL_0024: stloc.2
IL_0025: ldloc.2
IL_0026: stloc.1
IL_0027: ldc.i4.1
IL_0028: br.s IL_002b
IL_002a: ldc.i4.0
IL_002b: call ""void System.Console.WriteLine(bool)""
IL_0030: nop
IL_0031: ldloc.0
IL_0032: stloc.s V_6
IL_0034: ldloc.s V_6
IL_0036: stloc.s V_4
IL_0038: ldloc.s V_4
IL_003a: isinst ""Derived""
IL_003f: brfalse.s IL_005a
IL_0041: ldloc.s V_4
IL_0043: castclass ""Derived""
IL_0048: stloc.s V_5
IL_004a: br.s IL_004c
IL_004c: ldloc.s V_5
IL_004e: stloc.3
IL_004f: br.s IL_0051
IL_0051: ldc.i4.1
IL_0052: call ""void System.Console.WriteLine(bool)""
IL_0057: nop
IL_0058: br.s IL_005a
IL_005a: ldloc.0
IL_005b: isinst ""Derived""
IL_0060: ldnull
IL_0061: cgt.un
IL_0063: call ""void System.Console.WriteLine(bool)""
IL_0068: nop
IL_0069: ret
IL_001c: stloc.2
IL_001d: ldloc.2
IL_001e: brfalse.s IL_0025
IL_0020: ldloc.2
IL_0021: stloc.1
IL_0022: ldc.i4.1
IL_0023: br.s IL_0026
IL_0025: ldc.i4.0
IL_0026: call ""void System.Console.WriteLine(bool)""
IL_002b: nop
IL_002c: ldloc.0
IL_002d: stloc.s V_6
IL_002f: ldloc.s V_6
IL_0031: stloc.s V_5
IL_0033: ldloc.s V_5
IL_0035: isinst ""Derived""
IL_003a: stloc.s V_4
IL_003c: ldloc.s V_4
IL_003e: brtrue.s IL_0042
IL_0040: br.s IL_0050
IL_0042: ldloc.s V_4
IL_0044: stloc.3
IL_0045: br.s IL_0047
IL_0047: ldc.i4.1
IL_0048: call ""void System.Console.WriteLine(bool)""
IL_004d: nop
IL_004e: br.s IL_0050
IL_0050: ldloc.0
IL_0051: isinst ""Derived""
IL_0056: ldnull
IL_0057: cgt.un
IL_0059: call ""void System.Console.WriteLine(bool)""
IL_005e: nop
IL_005f: ret
}"
);
}
...
...
@@ -752,26 +750,22 @@ .locals init (bool? V_0)
}"
);
compVerifier
.
VerifyIL
(
"C.M2"
,
@"{
// Code size
26 (0x1a
)
// Code size
19 (0x13
)
.maxstack 1
.locals init (object V_0,
string V_1)
.locals init (string V_0)
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: isinst ""string""
IL_0008: brfalse.s IL_0018
IL_000a: ldloc.0
IL_000b: castclass ""string""
IL_0010: stloc.1
IL_0011: ldarg.1
IL_0012: brfalse.s IL_0016
IL_0014: ldc.i4.1
IL_0015: ret
IL_0016: ldc.i4.2
IL_0017: ret
IL_0018: ldc.i4.3
IL_0019: ret
IL_0001: isinst ""string""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0011
IL_000a: ldarg.1
IL_000b: brfalse.s IL_000f
IL_000d: ldc.i4.1
IL_000e: ret
IL_000f: ldc.i4.2
IL_0010: ret
IL_0011: ldc.i4.3
IL_0012: ret
}"
);
}
...
...
@@ -825,42 +819,41 @@ public void SwitchBasedPatternMatching(object o)
var
compVerifier
=
CompileAndVerify
(
compilation
);
compVerifier
.
VerifyIL
(
"C.SwitchBasedPatternMatching"
,
@"{
// Code size 7
7 (0x4d
)
// Code size 7
1 (0x47
)
.maxstack 2
.locals init (
objec
t V_0,
int
V_1,
string
V_2)
.locals init (
in
t V_0,
string
V_1,
object
V_2)
IL_0000: ldarg.1
IL_0001: stloc.
0
IL_0002: ldloc.
0
IL_0001: stloc.
2
IL_0002: ldloc.
2
IL_0003: isinst ""int""
IL_0008: brfalse.s IL_0013
IL_000a: ldloc.
0
IL_000a: ldloc.
2
IL_000b: unbox.any ""int""
IL_0010: stloc.
1
IL_0011: br.s IL_00
24
IL_0013: ldloc.
0
IL_0010: stloc.
0
IL_0011: br.s IL_00
1e
IL_0013: ldloc.
2
IL_0014: isinst ""string""
IL_0019: brfalse.s IL_004c
IL_001b: ldloc.0
IL_001c: castclass ""string""
IL_0021: stloc.2
IL_0022: br.s IL_0033
IL_0024: ldloc.1
IL_0025: ldc.i4.1
IL_0026: bne.un.s IL_003e
IL_0028: ldstr ""1""
IL_002d: call ""void System.Console.WriteLine(string)""
IL_0032: ret
IL_0033: ldstr ""s""
IL_0038: call ""void System.Console.WriteLine(string)""
IL_003d: ret
IL_003e: ldloc.1
IL_003f: ldc.i4.2
IL_0040: bne.un.s IL_004c
IL_0042: ldstr ""2""
IL_0047: call ""void System.Console.WriteLine(string)""
IL_004c: ret
IL_0019: stloc.1
IL_001a: ldloc.1
IL_001b: brtrue.s IL_002d
IL_001d: ret
IL_001e: ldloc.0
IL_001f: ldc.i4.1
IL_0020: bne.un.s IL_0038
IL_0022: ldstr ""1""
IL_0027: call ""void System.Console.WriteLine(string)""
IL_002c: ret
IL_002d: ldstr ""s""
IL_0032: call ""void System.Console.WriteLine(string)""
IL_0037: ret
IL_0038: ldloc.0
IL_0039: ldc.i4.2
IL_003a: bne.un.s IL_0046
IL_003c: ldstr ""2""
IL_0041: call ""void System.Console.WriteLine(string)""
IL_0046: ret
}"
);
}
...
...
@@ -971,6 +964,201 @@ .locals init (U V_0)
IL_001a: box ""T""
IL_001f: call ""void System.Console.Write(object)""
IL_0024: ret
}"
);
}
[
Fact
,
WorkItem
(
20641
,
"https://github.com/dotnet/roslyn/issues/20641"
)]
public
void
PatternsVsAs01
()
{
var
source
=
@"using System.Collections;
using System.Collections.Generic;
class Program
{
static void Main() { }
internal static bool TryGetCount1<T>(IEnumerable<T> source, out int count)
{
ICollection nonGeneric = source as ICollection;
if (nonGeneric != null)
{
count = nonGeneric.Count;
return true;
}
ICollection<T> generic = source as ICollection<T>;
if (generic != null)
{
count = generic.Count;
return true;
}
count = -1;
return false;
}
internal static bool TryGetCount2<T>(IEnumerable<T> source, out int count)
{
switch (source)
{
case ICollection nonGeneric:
count = nonGeneric.Count;
return true;
case ICollection<T> generic:
count = generic.Count;
return true;
default:
count = -1;
return false;
}
}
}"
;
var
compilation
=
CreateCompilation
(
source
,
options
:
TestOptions
.
ReleaseDll
);
compilation
.
VerifyDiagnostics
();
var
compVerifier
=
CompileAndVerify
(
compilation
);
compVerifier
.
VerifyIL
(
"Program.TryGetCount1<T>"
,
@"{
// Code size 45 (0x2d)
.maxstack 2
.locals init (System.Collections.ICollection V_0, //nonGeneric
System.Collections.Generic.ICollection<T> V_1) //generic
IL_0000: ldarg.0
IL_0001: isinst ""System.Collections.ICollection""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0014
IL_000a: ldarg.1
IL_000b: ldloc.0
IL_000c: callvirt ""int System.Collections.ICollection.Count.get""
IL_0011: stind.i4
IL_0012: ldc.i4.1
IL_0013: ret
IL_0014: ldarg.0
IL_0015: isinst ""System.Collections.Generic.ICollection<T>""
IL_001a: stloc.1
IL_001b: ldloc.1
IL_001c: brfalse.s IL_0028
IL_001e: ldarg.1
IL_001f: ldloc.1
IL_0020: callvirt ""int System.Collections.Generic.ICollection<T>.Count.get""
IL_0025: stind.i4
IL_0026: ldc.i4.1
IL_0027: ret
IL_0028: ldarg.1
IL_0029: ldc.i4.m1
IL_002a: stind.i4
IL_002b: ldc.i4.0
IL_002c: ret
}"
);
compVerifier
.
VerifyIL
(
"Program.TryGetCount2<T>"
,
@"{
// Code size 56 (0x38)
.maxstack 2
.locals init (System.Collections.ICollection V_0, //nonGeneric
System.Collections.Generic.ICollection<T> V_1, //generic
System.Collections.ICollection V_2,
System.Collections.Generic.ICollection<T> V_3,
System.Collections.Generic.IEnumerable<T> V_4)
IL_0000: ldarg.0
IL_0001: stloc.s V_4
IL_0003: ldloc.s V_4
IL_0005: isinst ""System.Collections.ICollection""
IL_000a: stloc.2
IL_000b: ldloc.2
IL_000c: brtrue.s IL_001b
IL_000e: ldloc.s V_4
IL_0010: isinst ""System.Collections.Generic.ICollection<T>""
IL_0015: stloc.3
IL_0016: ldloc.3
IL_0017: brtrue.s IL_0027
IL_0019: br.s IL_0033
IL_001b: ldloc.2
IL_001c: stloc.0
IL_001d: ldarg.1
IL_001e: ldloc.0
IL_001f: callvirt ""int System.Collections.ICollection.Count.get""
IL_0024: stind.i4
IL_0025: ldc.i4.1
IL_0026: ret
IL_0027: ldloc.3
IL_0028: stloc.1
IL_0029: ldarg.1
IL_002a: ldloc.1
IL_002b: callvirt ""int System.Collections.Generic.ICollection<T>.Count.get""
IL_0030: stind.i4
IL_0031: ldc.i4.1
IL_0032: ret
IL_0033: ldarg.1
IL_0034: ldc.i4.m1
IL_0035: stind.i4
IL_0036: ldc.i4.0
IL_0037: ret
}"
);
}
[
Fact
,
WorkItem
(
20641
,
"https://github.com/dotnet/roslyn/issues/20641"
)]
public
void
PatternsVsAs02
()
{
var
source
=
@"using System.Collections;
class Program
{
static void Main() { }
internal static bool IsEmpty1(IEnumerable source)
{
var c = source as ICollection;
return c != null && c.Count > 0;
}
internal static bool IsEmpty2(IEnumerable source)
{
return source is ICollection c && c.Count > 0;
}
}"
;
var
compilation
=
CreateCompilation
(
source
,
options
:
TestOptions
.
ReleaseDll
);
compilation
.
VerifyDiagnostics
();
var
compVerifier
=
CompileAndVerify
(
compilation
);
compVerifier
.
VerifyIL
(
"Program.IsEmpty1"
,
@"{
// Code size 22 (0x16)
.maxstack 2
.locals init (System.Collections.ICollection V_0) //c
IL_0000: ldarg.0
IL_0001: isinst ""System.Collections.ICollection""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0014
IL_000a: ldloc.0
IL_000b: callvirt ""int System.Collections.ICollection.Count.get""
IL_0010: ldc.i4.0
IL_0011: cgt
IL_0013: ret
IL_0014: ldc.i4.0
IL_0015: ret
}"
);
compVerifier
.
VerifyIL
(
"Program.IsEmpty2"
,
@"{
// Code size 24 (0x18)
.maxstack 2
.locals init (System.Collections.ICollection V_0, //c
System.Collections.ICollection V_1)
IL_0000: ldarg.0
IL_0001: isinst ""System.Collections.ICollection""
IL_0006: stloc.1
IL_0007: ldloc.1
IL_0008: brfalse.s IL_0016
IL_000a: ldloc.1
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: callvirt ""int System.Collections.ICollection.Count.get""
IL_0012: ldc.i4.0
IL_0013: cgt
IL_0015: ret
IL_0016: ldc.i4.0
IL_0017: ret
}"
);
}
}
...
...
src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs
浏览文件 @
2021715c
...
...
@@ -7941,22 +7941,22 @@ .locals init (int? V_0)
// Code size 51 (0x33)
.maxstack 1
.locals init (int V_0, //i
int
?
V_1,
int V_2,
int V_1,
int
?
V_2,
int? V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.
1
IL_0005: ldloca.s V_
1
IL_0004: stloc.
2
IL_0005: ldloca.s V_
2
IL_0007: call ""bool int?.HasValue.get""
IL_000c: brfalse.s IL_0025
IL_000e: ldloca.s V_
1
IL_000e: ldloca.s V_
2
IL_0010: call ""int int?.GetValueOrDefault()""
IL_0015: stloc.
2
IL_0015: stloc.
1
IL_0016: br.s IL_0018
IL_0018: ldloc.
2
IL_0018: ldloc.
1
IL_0019: stloc.0
IL_001a: br.s IL_001c
IL_001c: ldloc.0
...
...
@@ -8088,22 +8088,22 @@ .locals init (int? V_0)
// Code size 37 (0x25)
.maxstack 1
.locals init (System.IComparable V_0, //i
int?
V_1,
System.IComparable
V_2,
System.IComparable
V_1,
int?
V_2,
int? V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.
1
IL_0005: ldloca.s V_
1
IL_0004: stloc.
2
IL_0005: ldloca.s V_
2
IL_0007: call ""bool int?.HasValue.get""
IL_000c: brfalse.s IL_0024
IL_000e: ldloc.
1
IL_000e: ldloc.
2
IL_000f: box ""int?""
IL_0014: stloc.
2
IL_0014: stloc.
1
IL_0015: br.s IL_0017
IL_0017: ldloc.
2
IL_0017: ldloc.
1
IL_0018: stloc.0
IL_0019: br.s IL_001b
IL_001b: ldloc.0
...
...
@@ -8165,22 +8165,22 @@ .locals init (object V_0)
// Code size 36 (0x24)
.maxstack 1
.locals init (int V_0, //i
objec
t V_1,
in
t V_2,
in
t V_1,
objec
t V_2,
object V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.
1
IL_0005: ldloc.
1
IL_0004: stloc.
2
IL_0005: ldloc.
2
IL_0006: isinst ""int""
IL_000b: brfalse.s IL_0023
IL_000d: ldloc.
1
IL_000d: ldloc.
2
IL_000e: unbox.any ""int""
IL_0013: stloc.
2
IL_0013: stloc.
1
IL_0014: br.s IL_0016
IL_0016: ldloc.
2
IL_0016: ldloc.
1
IL_0017: stloc.0
IL_0018: br.s IL_001a
IL_001a: ldloc.0
...
...
@@ -8245,22 +8245,22 @@ .locals init (object V_0)
// Code size 41 (0x29)
.maxstack 1
.locals init (T V_0, //i
object
V_1,
T
V_2,
T
V_1,
object
V_2,
object V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.
1
IL_0005: ldloc.
1
IL_0004: stloc.
2
IL_0005: ldloc.
2
IL_0006: isinst ""T""
IL_000b: brfalse.s IL_0028
IL_000d: ldloc.
1
IL_000d: ldloc.
2
IL_000e: unbox.any ""T""
IL_0013: stloc.
2
IL_0013: stloc.
1
IL_0014: br.s IL_0016
IL_0016: ldloc.
2
IL_0016: ldloc.
1
IL_0017: stloc.0
IL_0018: br.s IL_001a
IL_001a: ldloc.0
...
...
@@ -8326,22 +8326,22 @@ .locals init (System.IComparable V_0)
// Code size 41 (0x29)
.maxstack 1
.locals init (T V_0, //i
System.IComparable
V_1,
T
V_2,
T
V_1,
System.IComparable
V_2,
System.IComparable V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.
1
IL_0005: ldloc.
1
IL_0004: stloc.
2
IL_0005: ldloc.
2
IL_0006: isinst ""T""
IL_000b: brfalse.s IL_0028
IL_000d: ldloc.
1
IL_000d: ldloc.
2
IL_000e: unbox.any ""T""
IL_0013: stloc.
2
IL_0013: stloc.
1
IL_0014: br.s IL_0016
IL_0016: ldloc.
2
IL_0016: ldloc.
1
IL_0017: stloc.0
IL_0018: br.s IL_001a
IL_001a: ldloc.0
...
...
@@ -8409,24 +8409,24 @@ .locals init (U V_0)
// Code size 51 (0x33)
.maxstack 1
.locals init (T V_0, //i
U
V_1,
T
V_2,
T
V_1,
U
V_2,
U V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.
1
IL_0005: ldloc.
1
IL_0004: stloc.
2
IL_0005: ldloc.
2
IL_0006: box ""U""
IL_000b: isinst ""T""
IL_0010: brfalse.s IL_0032
IL_0012: ldloc.
1
IL_0012: ldloc.
2
IL_0013: box ""U""
IL_0018: unbox.any ""T""
IL_001d: stloc.
2
IL_001d: stloc.
1
IL_001e: br.s IL_0020
IL_0020: ldloc.
2
IL_0020: ldloc.
1
IL_0021: stloc.0
IL_0022: br.s IL_0024
IL_0024: ldloc.0
...
...
@@ -8603,8 +8603,8 @@ .maxstack 1
// Code size 53 (0x35)
.maxstack 1
.locals init (T V_0, //t
System.ValueType
V_1,
T
V_2,
T
V_1,
System.ValueType
V_2,
System.ValueType V_3,
T V_4,
T V_5)
...
...
@@ -8612,16 +8612,16 @@ .maxstack 1
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.
1
IL_0005: ldloc.
1
IL_0004: stloc.
2
IL_0005: ldloc.
2
IL_0006: isinst ""T""
IL_000b: brfalse.s IL_0024
IL_000d: ldloc.
1
IL_000d: ldloc.
2
IL_000e: isinst ""T""
IL_0013: unbox.any ""T""
IL_0018: stloc.
2
IL_0018: stloc.
1
IL_0019: br.s IL_001b
IL_001b: ldloc.
2
IL_001b: ldloc.
1
IL_001c: stloc.0
IL_001d: br.s IL_001f
IL_001f: ldloc.0
...
...
@@ -8758,26 +8758,26 @@ .maxstack 1
// Code size 54 (0x36)
.maxstack 1
.locals init (int V_0, //t
T
V_1,
int
V_2,
int
V_1,
T
V_2,
T V_3,
int V_4)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.
1
IL_0005: ldloc.
1
IL_0004: stloc.
2
IL_0005: ldloc.
2
IL_0006: box ""T""
IL_000b: isinst ""int""
IL_0010: brfalse.s IL_002e
IL_0012: ldloc.
1
IL_0012: ldloc.
2
IL_0013: box ""T""
IL_0018: isinst ""int""
IL_001d: unbox.any ""int""
IL_0022: stloc.
2
IL_0022: stloc.
1
IL_0023: br.s IL_0025
IL_0025: ldloc.
2
IL_0025: ldloc.
1
IL_0026: stloc.0
IL_0027: br.s IL_0029
IL_0029: ldloc.0
...
...
@@ -9302,16 +9302,16 @@ public static void M(object o)
@"{
// Code size 106 (0x6a)
.maxstack 1
.locals init (
objec
t V_0,
in
t V_1)
.locals init (
in
t V_0,
objec
t V_1)
IL_0000: ldarg.0
IL_0001: stloc.
0
IL_0002: ldloc.
0
IL_0001: stloc.
1
IL_0002: ldloc.
1
IL_0003: isinst ""int""
IL_0008: brfalse.s IL_0021
IL_000a: ldloc.
0
IL_000a: ldloc.
1
IL_000b: unbox.any ""int""
IL_0010: stloc.
1
IL_0010: stloc.
0
IL_0011: ldsfld ""bool Program.b""
IL_0016: brtrue.s IL_0069
IL_0018: ldsfld ""bool Program.b""
...
...
@@ -9355,22 +9355,22 @@ .maxstack 1
int V_1, //i
int V_2, //i
int V_3, //i
objec
t V_4,
in
t V_5,
in
t V_4,
objec
t V_5,
object V_6)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.s V_6
IL_0004: ldloc.s V_6
IL_0006: stloc.s V_
4
IL_0008: ldloc.s V_
4
IL_0006: stloc.s V_
5
IL_0008: ldloc.s V_
5
IL_000a: isinst ""int""
IL_000f: brfalse.s IL_0033
IL_0011: ldloc.s V_
4
IL_0011: ldloc.s V_
5
IL_0013: unbox.any ""int""
IL_0018: stloc.s V_
5
IL_0018: stloc.s V_
4
IL_001a: br.s IL_001c
IL_001c: ldloc.s V_
5
IL_001c: ldloc.s V_
4
IL_001e: stloc.0
IL_001f: ldsfld ""bool Program.b""
IL_0024: brtrue.s IL_0028
...
...
@@ -9383,7 +9383,7 @@ .maxstack 1
IL_0038: brtrue.s IL_003c
IL_003a: br.s IL_0055
IL_003c: br.s IL_009b
IL_003e: ldloc.s V_
5
IL_003e: ldloc.s V_
4
IL_0040: stloc.1
IL_0041: ldsfld ""bool Program.b""
IL_0046: brtrue.s IL_004a
...
...
@@ -9396,7 +9396,7 @@ .maxstack 1
IL_005a: brtrue.s IL_005e
IL_005c: br.s IL_0077
IL_005e: br.s IL_009b
IL_0060: ldloc.s V_
5
IL_0060: ldloc.s V_
4
IL_0062: stloc.2
IL_0063: ldsfld ""bool Program.b""
IL_0068: brtrue.s IL_006c
...
...
@@ -9409,7 +9409,7 @@ .maxstack 1
IL_007c: brtrue.s IL_0080
IL_007e: br.s IL_0090
IL_0080: br.s IL_009b
IL_0082: ldloc.s V_
5
IL_0082: ldloc.s V_
4
IL_0084: stloc.3
IL_0085: ldsfld ""bool Program.b""
IL_008a: brtrue.s IL_008e
...
...
src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs
浏览文件 @
2021715c
...
...
@@ -2442,44 +2442,44 @@ .maxstack 2
int V_1, //i
int V_2, //j
object V_3, //o
objec
t V_4,
int
V_5,
byte
V_6,
in
t V_4,
byte
V_5,
object
V_6,
object V_7)
IL_0000: nop
IL_0001: call ""object C.F()""
IL_0006: stloc.s V_7
IL_0008: ldloc.s V_7
IL_000a: stloc.s V_
4
IL_000c: ldloc.s V_
4
IL_000a: stloc.s V_
6
IL_000c: ldloc.s V_
6
IL_000e: isinst ""int""
IL_0013: brfalse.s IL_0025
IL_0015: ldloc.s V_
4
IL_0015: ldloc.s V_
6
IL_0017: unbox.any ""int""
IL_001c: stloc.s V_
5
IL_001c: stloc.s V_
4
IL_001e: ldc.i4.1
IL_001f: ldloc.s V_
5
IL_001f: ldloc.s V_
4
IL_0021: beq.s IL_0046
IL_0023: br.s IL_0068
IL_0025: ldloc.s V_
4
IL_0025: ldloc.s V_
6
IL_0027: isinst ""byte""
IL_002c: brfalse.s IL_0040
IL_002e: ldloc.s V_
4
IL_002e: ldloc.s V_
6
IL_0030: unbox.any ""byte""
IL_0035: stloc.s V_
6
IL_0035: stloc.s V_
5
IL_0037: br.s IL_0053
IL_0039: ldc.i4.1
IL_003a: ldloc.s V_
6
IL_003a: ldloc.s V_
5
IL_003c: beq.s IL_007d
IL_003e: br.s IL_0098
IL_0040: ldloc.s V_
4
IL_0040: ldloc.s V_
6
IL_0042: brtrue.s IL_0098
IL_0044: br.s IL_00a6
IL_0046: ldstr ""int 1""
IL_004b: call ""void System.Console.WriteLine(string)""
IL_0050: nop
IL_0051: br.s IL_00a6
IL_0053: ldloc.s V_
6
IL_0053: ldloc.s V_
5
IL_0055: stloc.0
IL_0056: call ""bool C.P()""
IL_005b: brtrue.s IL_005f
...
...
@@ -2488,7 +2488,7 @@ .maxstack 2
IL_0060: call ""void System.Console.WriteLine(int)""
IL_0065: nop
IL_0066: br.s IL_00a6
IL_0068: ldloc.s V_
5
IL_0068: ldloc.s V_
4
IL_006a: stloc.1
IL_006b: call ""bool C.P()""
IL_0070: brtrue.s IL_0074
...
...
@@ -2501,14 +2501,14 @@ .maxstack 2
IL_0082: call ""void System.Console.WriteLine(string)""
IL_0087: nop
IL_0088: br.s IL_00a6
IL_008a: ldloc.s V_
5
IL_008a: ldloc.s V_
4
IL_008c: stloc.2
IL_008d: br.s IL_008f
IL_008f: ldloc.2
IL_0090: call ""void System.Console.WriteLine(int)""
IL_0095: nop
IL_0096: br.s IL_00a6
IL_0098: ldloc.s V_
4
IL_0098: ldloc.s V_
6
IL_009a: stloc.3
IL_009b: br.s IL_009d
IL_009d: ldloc.3
...
...
@@ -2534,47 +2534,47 @@ .maxstack 2
int V_1, //i
int V_2, //j
object V_3, //o
[
objec
t] V_4,
[
int
] V_5,
[
unchanged
] V_6,
[
in
t] V_4,
[
unchanged
] V_5,
[
object
] V_6,
object V_7,
objec
t V_8,
int
V_9,
byte
V_10)
in
t V_8,
byte
V_9,
object
V_10)
IL_0000: nop
IL_0001: call ""object C.F()""
IL_0006: stloc.s V_7
IL_0008: ldloc.s V_7
IL_000a: stloc.s V_
8
IL_000c: ldloc.s V_
8
IL_000a: stloc.s V_
10
IL_000c: ldloc.s V_
10
IL_000e: isinst ""int""
IL_0013: brfalse.s IL_0025
IL_0015: ldloc.s V_
8
IL_0015: ldloc.s V_
10
IL_0017: unbox.any ""int""
IL_001c: stloc.s V_
9
IL_001c: stloc.s V_
8
IL_001e: ldc.i4.1
IL_001f: ldloc.s V_
9
IL_001f: ldloc.s V_
8
IL_0021: beq.s IL_0046
IL_0023: br.s IL_0068
IL_0025: ldloc.s V_
8
IL_0025: ldloc.s V_
10
IL_0027: isinst ""byte""
IL_002c: brfalse.s IL_0040
IL_002e: ldloc.s V_
8
IL_002e: ldloc.s V_
10
IL_0030: unbox.any ""byte""
IL_0035: stloc.s V_
10
IL_0035: stloc.s V_
9
IL_0037: br.s IL_0053
IL_0039: ldc.i4.1
IL_003a: ldloc.s V_
10
IL_003a: ldloc.s V_
9
IL_003c: beq.s IL_007d
IL_003e: br.s IL_0098
IL_0040: ldloc.s V_
8
IL_0040: ldloc.s V_
10
IL_0042: brtrue.s IL_0098
IL_0044: br.s IL_00a6
IL_0046: ldstr ""int 1""
IL_004b: call ""void System.Console.WriteLine(string)""
IL_0050: nop
IL_0051: br.s IL_00a6
IL_0053: ldloc.s V_
10
IL_0053: ldloc.s V_
9
IL_0055: stloc.0
IL_0056: call ""bool C.P()""
IL_005b: brtrue.s IL_005f
...
...
@@ -2583,7 +2583,7 @@ .maxstack 2
IL_0060: call ""void System.Console.WriteLine(int)""
IL_0065: nop
IL_0066: br.s IL_00a6
IL_0068: ldloc.s V_
9
IL_0068: ldloc.s V_
8
IL_006a: stloc.1
IL_006b: call ""bool C.P()""
IL_0070: brtrue.s IL_0074
...
...
@@ -2596,14 +2596,14 @@ .maxstack 2
IL_0082: call ""void System.Console.WriteLine(string)""
IL_0087: nop
IL_0088: br.s IL_00a6
IL_008a: ldloc.s V_
9
IL_008a: ldloc.s V_
8
IL_008c: stloc.2
IL_008d: br.s IL_008f
IL_008f: ldloc.2
IL_0090: call ""void System.Console.WriteLine(int)""
IL_0095: nop
IL_0096: br.s IL_00a6
IL_0098: ldloc.s V_
8
IL_0098: ldloc.s V_
10
IL_009a: stloc.3
IL_009b: br.s IL_009d
IL_009d: ldloc.3
...
...
src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
浏览文件 @
2021715c
...
...
@@ -3417,8 +3417,8 @@ static void M2<T>()
.maxstack 1
.locals init (T V_0, //t
int V_1, //i
int
V_2,
T
V_3,
T
V_2,
int
V_3,
int V_4)
// sequence point: {
IL_0000: nop
...
...
@@ -3426,18 +3426,18 @@ .maxstack 1
IL_0001: ldc.i4.1
IL_0002: stloc.s V_4
IL_0004: ldc.i4.1
IL_0005: stloc.
2
IL_0006: ldloc.
2
IL_0005: stloc.
3
IL_0006: ldloc.
3
IL_0007: box ""int""
IL_000c: isinst ""T""
IL_0011: brfalse.s IL_0033
IL_0013: ldloc.
2
IL_0013: ldloc.
3
IL_0014: box ""int""
IL_0019: isinst ""T""
IL_001e: unbox.any ""T""
IL_0023: stloc.
3
IL_0023: stloc.
2
IL_0024: br.s IL_0026
IL_0026: ldloc.
3
IL_0026: ldloc.
2
IL_0027: stloc.0
IL_0028: br.s IL_002a
// sequence point: Console.Write(1);
...
...
@@ -3446,7 +3446,7 @@ .maxstack 1
IL_0030: nop
// sequence point: break;
IL_0031: br.s IL_0040
IL_0033: ldloc.
2
IL_0033: ldloc.
3
IL_0034: stloc.1
IL_0035: br.s IL_0037
// sequence point: Console.Write(2);
...
...
@@ -3457,15 +3457,16 @@ .maxstack 1
IL_003e: br.s IL_0040
// sequence point: }
IL_0040: ret
}"
);
}
"
);
verifier
.
VerifyIL
(
qualifiedMethodName
:
"Program.M2<T>"
,
sequencePoints
:
"Program.M2"
,
source
:
source
,
expectedIL
:
@"{
// Code size 63 (0x3f)
.maxstack 1
.locals init (T V_0, //t
string V_1, //s
string
V_2,
T
V_3,
T
V_2,
string
V_3,
string V_4)
// sequence point: {
IL_0000: nop
...
...
@@ -3473,16 +3474,16 @@ .maxstack 1
IL_0001: ldstr ""M2""
IL_0006: stloc.s V_4
IL_0008: ldstr ""M2""
IL_000d: stloc.
2
IL_000e: ldloc.
2
IL_000d: stloc.
3
IL_000e: ldloc.
3
IL_000f: isinst ""T""
IL_0014: brfalse.s IL_0031
IL_0016: ldloc.
2
IL_0016: ldloc.
3
IL_0017: isinst ""T""
IL_001c: unbox.any ""T""
IL_0021: stloc.
3
IL_0021: stloc.
2
IL_0022: br.s IL_0024
IL_0024: ldloc.
3
IL_0024: ldloc.
2
IL_0025: stloc.0
IL_0026: br.s IL_0028
// sequence point: Console.Write(3);
...
...
@@ -3491,7 +3492,7 @@ .maxstack 1
IL_002e: nop
// sequence point: break;
IL_002f: br.s IL_003e
IL_0031: ldloc.
2
IL_0031: ldloc.
3
IL_0032: stloc.1
IL_0033: br.s IL_0035
// sequence point: Console.Write(4);
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录