我有机会发现C#编译器会改变这个方法:
static bool IsNotNull(object obj)
{
return obj != null;
}
Run Code Online (Sandbox Code Playgroud)
...进入这个CIL:
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0 // obj
ldnull
cgt.un
ret
}
Run Code Online (Sandbox Code Playgroud)
...或者,如果您更喜欢查看反编译的C#代码:
static bool IsNotNull(object obj)
{
return obj > null; // (note: this is not a valid C# expression)
}
Run Code Online (Sandbox Code Playgroud)
怎么把它!=翻译成" >"?
我的问题涉及静态方法与实例方法的性能特征及其可伸缩性.假设在这种情况下,所有类定义都在一个程序集中,并且需要多个离散指针类型.
考虑:
public sealed class InstanceClass
{
public int DoOperation1(string input)
{
// Some operation.
}
public int DoOperation2(string input)
{
// Some operation.
}
// … more instance methods.
}
public static class StaticClass
{
public static int DoOperation1(string input)
{
// Some operation.
}
public static int DoOperation2(string input)
{
// Some operation.
}
// … more static methods.
}
Run Code Online (Sandbox Code Playgroud)
上述类表示辅助样式模式.
在实例类中,解析实例方法需要花费一些时间来与StaticClass相反.
我的问题是:
保持状态不是问题(不需要字段或属性),使用静态类总是更好吗?
如果有相当多的静态类定义(例如100,每个都有许多静态方法),与相同数量的实例类定义相比,这会对执行性能或内存消耗产生负面影响吗?
当调用同一实例类中的另一个方法时,实例解析是否仍然发生?例如,使用[this]关键字,例如this.DoOperation2("abc")来自DoOperation1同一实例.
我有一个性能关键的二元决策树,我想把这个问题集中在一行代码上.下面是二叉树迭代器的代码,其中包含针对它运行性能分析的结果.
public ScTreeNode GetNodeForState(int rootIndex, float[] inputs)
{
0.2% ScTreeNode node = RootNodes[rootIndex].TreeNode;
24.6% while (node.BranchData != null)
{
0.2% BranchNodeData b = node.BranchData;
0.5% node = b.Child2;
12.8% if (inputs[b.SplitInputIndex] <= b.SplitValue)
0.8% node = b.Child1;
}
0.4% return node;
}
Run Code Online (Sandbox Code Playgroud)
BranchData是一个字段,而不是属性.我这样做是为了防止它被内联的风险.
BranchNodeData类如下:
public sealed class BranchNodeData
{
/// <summary>
/// The index of the data item in the input array on which we need to split
/// </summary>
internal int SplitInputIndex = 0;
/// <summary>
/// The …Run Code Online (Sandbox Code Playgroud) 我在C#中有这两个代码块:
class Program
{
static Stack<int> S = new Stack<int>();
static int Foo(int n) {
if (n == 0)
return 0;
S.Push(0);
S.Push(1);
...
S.Push(999);
return Foo( n-1 );
}
}
Run Code Online (Sandbox Code Playgroud)
class Program
{
static Stack S = new Stack();
static int Foo(int n) {
if (n == 0)
return 0;
S.Push(0);
S.Push(1);
...
S.Push(999);
return Foo( n-1 );
}
}
Run Code Online (Sandbox Code Playgroud)
他们都这样做:
创建一个堆栈(<int>第一个示例为通用,第二个示例为对象堆栈).
声明一个递归调用n次(n> = 0)的方法,并在每个步骤中在创建的堆栈内推送1000个整数.
当我运行第一个例子时Foo(30000)没有发生异常,但是第二个例子崩溃了Foo(1000),只有n = 1000.
当我看到为两种情况生成的CIL时,唯一的区别是每次推送的拳击部分: …
下面是一个简单的测试夹具.它在Debug版本中成功,在Release版本中失败(VS2010,.NET4解决方案,x64):
[TestFixture]
public sealed class Test
{
[Test]
public void TestChecker()
{
var checker = new Checker();
Assert.That(checker.IsDateTime(DateTime.Now), Is.True);
}
}
public class Checker
{
public bool IsDateTime(object o)
{
return o is DateTime;
}
}
Run Code Online (Sandbox Code Playgroud)
似乎代码优化会造成一些破坏; 如果我在Release版本上禁用它,它也可以.这让我感到很困惑.下面,我使用ILDASM来反汇编构建的两个版本:
调试IL:
.method public hidebysig instance bool IsDateTime(object o) cil managed
{
// Code size 15 (0xf)
.maxstack 2
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: isinst [mscorlib]System.DateTime
IL_0007: ldnull
IL_0008: cgt.un
IL_000a: stloc.0
IL_000b: br.s IL_000d
IL_000d: ldloc.0
IL_000e: ret …Run Code Online (Sandbox Code Playgroud) 考虑以下代码:
var str = (string)null;
Run Code Online (Sandbox Code Playgroud)
编写代码时,这是我的IL代码:
IL_0001: ldnull
Run Code Online (Sandbox Code Playgroud)
并且IL有任何Cast操作符但是:
var test = (string) new Object();
Run Code Online (Sandbox Code Playgroud)
该IL代码是:
IL_0008: castclass [mscorlib]System.String
Run Code Online (Sandbox Code Playgroud)
所以Casting nullto string被忽略了.
为什么编译器允许我转换null为特定类型?
C#没有暴露哪些IL指令?
我指的是像sizeof和cpblk这样的指令 - 没有执行这些指令的类或命令(C#中的sizeof是在编译时计算的,而不是在运行时AFAIK计算的).
其他?
编辑:我问这个的原因(并希望这将使我的问题更有效)是因为我正在开发一个小型库,它将提供这些指令的功能.sizeof和cpblk已经实现 - 我想知道在继续之前我可能错过了什么.
编辑2:使用Eric的答案,我编写了一份说明列表:
列表中没有包含许多其他指令,我将它们分开,因为它们基本上是其他指令的快捷方式(压缩以节省时间和空间):
在反汇编.NET函数时,我注意到它们都是以类似于similair的模式开始的.这个初始代码有什么作用?
此代码出现在函数应该执行的实际代码之前.它是某种参数计数验证吗?
FUNC1
private static void Foo(int i)
{
Console.WriteLine("hello");
}
00000000 push ebp
00000001 mov ebp,esp
00000003 push eax
00000004 mov dword ptr [ebp-4],ecx
00000007 cmp dword ptr ds:[005C14A4h],0
0000000e je 00000015
00000010 call 65E0367F
//the console writleline code follows here and is not part of the question
Run Code Online (Sandbox Code Playgroud)
FUNC2
static private void Bar()
{
for (int i = 0; i < 1000; i++)
{
Foo(i);
}
}
00000000 push ebp
00000001 mov ebp,esp
00000003 push eax
00000004 cmp dword …Run Code Online (Sandbox Code Playgroud) 有时我想快速查看我在C#中的代码片段的IL表示,以了解底层各种代码语句究竟发生了什么,就像在这里完成的那样.
我知道有ildasm,Reflector,ILSpy,dotPeek以及其他一些可用的工具.我想知道的是,如果有更优雅的方式从编写一些代码行到查看相应的IL,那么比编译.net代码,将程序集加载到其中一个程序中并找到您感兴趣的代码.
也许有一个visual studio的插件,它会为"构建和查看IL代码"或任何其他方便的方式添加一个右键单击选项?
编辑:
经过一些谷歌搜索后,我找到了NDasm codeplex项目,它完成了我想要的东西 - 集成到visual studio中.
但vcsjones的LINQPad建议也很棒,因此我将其标记为已接受,谢谢.一个非常有用的工具......
是否可以查看在Expression树上调用Compile()时生成的IL代码?考虑这个非常简单的例子:
class Program
{
public int Value { get; set; }
static void Main(string[] args)
{
var param = Expression.Parameter(typeof(Program));
var con = Expression.Constant(5);
var prop = Expression.Property(param, typeof(Program).GetProperty("Value"));
var assign = Expression.Assign(prop, con);
Action<Program> lambda = Expression.Lambda<Action<Program>>(assign, param).Compile();
Program p = new Program();
lambda(p);
//p.Value = 5;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,表达式树执行最后一行Main所说的内容.编译应用程序,然后在Reflector中打开它.您可以看到p.Value = 5;执行分配的IL代码.但表达式树是在运行时编译和编译的.是否可以从编译中查看生成的IL代码?
c# ×10
il ×10
.net ×2
optimization ×2
performance ×2
casting ×1
cil ×1
disassembly ×1
instructions ×1
notnull ×1
null ×1