如果我在类中实现一个接口,它绝对不执行任何操作,它会减慢调用它的代码吗?示例2(NoLogger)对使用它的代码的速度有什么影响吗?
interface ILogger{
void Write(string text);
}
class TextLogger : ILogger {
public void Write(string text){
using (var sw = new StreamWriter(@"C:\log.txt"))
{
sw.WriteLine(text);
}
}
}
class NoLogger : ILogger{
public void Write(string text){
//Do absolutely nothing
}
}
void Main(){
ILogger tl = new TextLogger();
for (int i = 0; i < 100; i++)
{
tl.Write(i.ToString());
}
}
void Main(){
ILogger tl = new NoLogger();
for (int i = 0; i < 100; i++)
{
tl.Write(i.ToString());
}
}
当然,示例 1(文本记录器)会减慢其实现代码的执行速度,因为它实际上做了一些事情。
但是示例 2 呢?编译器是否足够聪明,能够弄清楚,即使实例化了一个类并调用了一个方法,也绝对没有任何代码可以在任何路径上执行任何操作,并且只是在编译时忽略它?
我认为我们可以将其概括为“JIT是否会内联接口方法的虚拟调用”——我强烈怀疑答案是否定的(要做到这一点,需要证明实现类型所涉及的只能是一种具体类型,这比我预期的分析要多)。
当然,你可以通过调用运行它 500,000,000 次,而不需要任何东西 - 然后你就会有一个 合理的起点 得到答案。
另请注意:即使
Write
不执行任何操作:仍然需要执行i.ToString()
,因为可能会产生副作用。
我怀疑你应该看看
[Conditional("...")]
属性。这确实改变了事情。很多。例如:
public static class Logger
{
[Conditional("TRACE")]
public static void Write(string text)
{
// some stuff
}
}
现在;如果我们在没有定义 TRACE
符号的情况下编译此 :
static void Main()
{
for (int i = 0; i < 100; i++)
{
Logger.Write(i.ToString());
}
}
我们的代码被编译为:
static void Main()
{
for (int i = 0; i < 100; i++)
{
}
}
该调用已被删除,而且:任何参数评估(
i.ToString()
)也被
也删除。如果我们使用定义的
TRACE
符号进行编译 - 那么代码就存在。编译器无法合法地完全消除调用。至少,编译器必须评估您传递给被调用方法的所有表达式 - 具体来说,在您的示例中
i.ToString()
将被调用 100 次,无论实现实际执行什么操作。编译器无法对其进行优化,因为否则在参数表达式具有副作用的情况下,它可能会意外更改程序的语义。