我不想讨论何时抛出异常而不抛出异常.我想解决一个简单的问题.99%的时间不抛出异常的论点围绕着它们缓慢而另一方声称(基准测试)速度不是问题.我读过很多关于一方或另一方的博客,文章和帖子.那是哪个呢?
遵循这个问题 - 使用C#传递方法作为参数和我的一些个人经验我想更多地了解调用委托与仅在C#中调用方法的性能.
虽然代表非常方便,但我有一个应用程序通过委托做了很多回调,当我们重写这个使用回调接口时,我们得到了一个数量级的速度提升.这是使用.NET 2.0所以我不确定3和4的情况如何变化.
如何在编译器/ CLR内部处理对委托的调用,这对方法调用的性能有何影响?
编辑 - 澄清代表与回调接口的含义.
对于异步调用,我的类可以提供一个OnComplete事件和调用者可以订阅的相关委托.
或者,我可以使用调用者实现的OnComplete方法创建一个ICallback接口,然后将其自身注册到类,然后在完成时调用该方法(即Java处理这些事情的方式).
我知道如何使用Action和Func在.NET中,但每次我开始,使用我调用的常规旧方法可以实现完全相同的解决方案.
这排除了当一个Action或者Func被用作我无法控制的东西的参数时,比如LINQ .Where.
基本上我的问题是......为什么这些存在?他们给了我什么额外的和新的一个简单的方法不?
首先,我要说我同意goto语句在很大程度上与现代编程语言中的更高级别结构无关,并且在适当的替代品可用时不应使用.
我最近重新阅读了Steve McConnell的Code Complete原版,并忘记了他对常见编码问题的建议.几年前,当我第一次开始时,我已经读过它,并且不认为我意识到配方会有多么有用.编码问题如下:执行循环时,您经常需要执行循环的一部分来初始化状态,然后使用其他逻辑执行循环,并使用相同的初始化逻辑结束每个循环.一个具体的例子是实现String.Join(delimiter,array)方法.
我想每个人第一个接受这个问题的都是这个.假设定义了append方法以将参数添加到返回值.
bool isFirst = true;
foreach (var element in array)
{
if (!isFirst)
{
append(delimiter);
}
else
{
isFirst = false;
}
append(element);
}
Run Code Online (Sandbox Code Playgroud)
注意:稍微优化一下就是删除else并将其放在循环的末尾.赋值通常是单个指令并等效于else,并将基本块的数量减少1并增加主要部分的基本块大小.结果是在每个循环中执行一个条件以确定是否应该添加分隔符.
我也看到并使用了其他处理这个常见循环问题的方法.您可以先在循环外执行初始元素代码,然后从第二个元素到结尾执行循环.您还可以将逻辑更改为始终附加元素然后添加分隔符,一旦完成循环,您只需删除添加的最后一个分隔符.
后一种解决方案往往是我更喜欢的解决方案,因为它不会复制任何代码.如果初始化序列的逻辑发生变化,您不必记得在两个地方修复它.然而,它需要额外的"工作"来做某事然后撤消它,至少导致额外的cpu周期,并且在很多情况下,例如我们的String.Join示例也需要额外的内存.
我兴奋地阅读这个结构
var enumerator = array.GetEnumerator();
if (enumerator.MoveNext())
{
goto start;
do {
append(delimiter);
start:
append(enumerator.Current);
} while (enumerator.MoveNext());
}
Run Code Online (Sandbox Code Playgroud)
这样做的好处是,您没有重复的代码,也没有额外的工作.你开始循环进入第一个循环的执行的一半,那就是你的初始化.您只能使用do while构造模拟其他循环,但转换很容易并且阅读并不困难.
所以,现在问题.我很高兴尝试将其添加到我正在处理的一些代码中,发现它不起作用.在C,C++,Basic中工作得很好但是在C#中你不能跳转到不是父范围的不同词法范围内的标签.我很失望.所以我想知道,在C#中处理这个非常常见的编码问题(我主要在字符串生成中看到它)的最佳方法是什么?
或许更具体的要求:
我认为可读性是唯一可能因我说的配方而受到影响的事情.但它在C#中不起作用,那么下一个最好的东西是什么?
*编辑* 由于一些讨论,我改变了我的表现标准.性能通常不是限制因素,所以更正确的目标应该是不合理,不是最快的.
我不喜欢我建议的替代实现的原因是因为它们要么重复代码,这留下了改变一个部分而不是另一个部分的空间,或者对于我通常选择它的那个需要"撤消"操作,这需要额外的思考和时间来撤消事物你做的那件事.特别是对于字符串操作,这通常会让您因一个错误而打开,或者无法解释一个空数组并尝试撤消未发生的事情.
我对编程很新,而且我对OOP的知识有限,我决定使用事件在我的课程之间进行交流.当然,这将导致相当多的事件.
我想知道使用事件是否还有额外的开销?我假设除非事件被执行(即,类中有一个监听器根据被触发的事件执行一个函数),那么实际上应该没有太大的影响.但是我并不熟悉C#中的事件,并且想要确认是否仅仅因为触发事件而产生了大量的额外开销?
请考虑以下代码:
if (IsDebuggingEnabled) {
instance.Log(GetDetailedDebugInfo());
}
Run Code Online (Sandbox Code Playgroud)
GetDetailedDebugInfo() 可能是一种昂贵的方法,所以我们只想在调试模式下运行时调用它.
现在,更清洁的替代方案是编写如下代码:
instance.Log(() => GetDetailedDebugInfo());
Run Code Online (Sandbox Code Playgroud)
其中.Log()的定义如下:
public void Log(Func<string> getMessage)
{
if (IsDebuggingEnabled)
{
LogInternal(getMessage.Invoke());
}
}
Run Code Online (Sandbox Code Playgroud)
我关心的是性能,初步测试没有显示第二种情况特别昂贵,但如果负载增加,我不想遇到任何意外.
哦,请不要建议条件编译,因为它不适用于这种情况.
(PS:我直接在StackOverflow中写了代码问一个问题textarea所以不要责怪我,如果有微妙的错误,它没有编译,你明白了:)