-
C#教程之面向接口编程,你考虑过性能吗?
本站最新发布 C#从入门到精通
试听地址 https://www.xin3721.com/eschool/CSharpxin3721/
试听地址 https://www.xin3721.com/eschool/CSharpxin3721/
大家在平时开发中大多都会遵循接口编程,这样就可以方便实现依赖注入也方便实现多态等各种小技巧,但这种是以牺牲性能为代价换取代码的灵活性,万物皆有阴阳,看你的应用场景进行取舍。
一:背景
1. 缘由
在项目的性能改造中,发现很多方法签名的返回值都是采用IEnumerable接口,比如下面这段代码:
public static void Main(string[] args)
{
var list = GetHasEmailCustomerIDList();
foreach (var item in list){}
Console.ReadLine();
}
public static IEnumerable<int> GetHasEmailCustomerIDList()
{
return Enumerable.Range(1, 5000000).ToArray();
}
2. 有什么问题
这段代码乍一看也没啥什么性能问题,foreach迭代天经地义,这个还能怎么优化???
<1> 从MSIL中寻找问题
首先我们尽可能把原貌还原出来,简化后的MSIL如下。
.method public hidebysig static
void Main (
string[] args
) cil managed
{
IL_0009: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
IL_000e: stloc.1
.try
{
IL_000f: br.s IL_001a
// loop start (head: IL_001a)
IL_0011: ldloc.1
IL_0012: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
IL_0017: stloc.2
IL_0018: nop
IL_0019: nop
IL_001a: ldloc.1
IL_001b: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0020: brtrue.s IL_0011
// end loop
IL_0022: leave.s IL_002f
} // end .try
finally
{
IL_0024: ldloc.1
IL_0025: brfalse.s IL_002e
IL_0027: ldloc.1
IL_0028: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002d: nop
IL_002e: endfinally
} // end handler
IL_002f: ret
} // end of method Program::Main
从IL中看到了标准的get_Current,MoveNext,Dispose
还有一个try,finally
,一下子多了这么多方法和关键词,不就是一个简单的foreach迭代数组嘛? 至于搞的这么复杂嘛?这样在大数据下怎么快的起来?
还有一个奇葩的事,如果你仔细观察IL代码,比如这句:[mscorlib]System.Collections.Generic.IEnumerable``1<int32>::GetEnumerator()
, 这个GetEnumerator前面是接口IEnumerable,正常情况下应该是具体迭代类吧,按理说应该会调用Array的GetEnumerator方法,如下所示。
[Serializable]
[ComVisible(true)]
[__DynamicallyInvokable]
public abstract class Array : ICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable
{
[__DynamicallyInvokable]
public IEnumerator GetEnumerator()
{
int lowerBound = GetLowerBound(0);
if (Rank == 1 && lowerBound == 0)
{
return new SZArrayEnumerator(this);
}
return new ArrayEnumerator(this, lowerBound, Length);
}
}
<2> 从windbg中寻找问题
IL中发现的第二个问题我特别好奇,
栏目列表
最新更新
C# 面向对象
假设客车的座位数是9行4列,使用二维数
C#基于接口设计三层架构Unity篇
C#线程 入门
C#读取静态类常量属性和值
C# 插件式编程
C# 委托与事件有啥区别?
C#队列学习笔记:队列(Queue)和堆栈(Stack
linq 多表分组左连接查询查询统计
C#队列学习笔记:MSMQ入门一
C# 在Word中添加Latex 数学公式和符号
inncheck命令 – 检查语法
基于UDP的服务器端和客户端
再谈UDP和TCP
在socket编程中使用域名
网络数据传输时的大小端问题
socket编程实现文件传输功能
如何优雅地断开TCP连接?
图解TCP四次握手断开连接
详细分析TCP数据的传输过程
SqlServer 利用游标批量更新数据
BOS只读状态修改
SQL Server等待事件—PAGEIOLATCH_EX
数据库多行转换为单一列
获取数据表最后最后访问,修改,更新,
计算经历的时间
SQL查询结果自定义排序
修改数据库默认位置
日期简单加或减
从日期获取年,月或日