Pet*_*ert 22 c# goto initialization do-while
首先,我要说我同意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#中不起作用,那么下一个最好的东西是什么?
*编辑* 由于一些讨论,我改变了我的表现标准.性能通常不是限制因素,所以更正确的目标应该是不合理,不是最快的.
我不喜欢我建议的替代实现的原因是因为它们要么重复代码,这留下了改变一个部分而不是另一个部分的空间,或者对于我通常选择它的那个需要"撤消"操作,这需要额外的思考和时间来撤消事物你做的那件事.特别是对于字符串操作,这通常会让您因一个错误而打开,或者无法解释一个空数组并尝试撤消未发生的事情.
Jon*_*eet 18
就个人而言,我喜欢Mark Byer的选择,但您总是可以为此编写自己的通用方法:
public static void IterateWithSpecialFirst<T>(this IEnumerable<T> source,
Action<T> firstAction,
Action<T> subsequentActions)
{
using (IEnumerator<T> iterator = source.GetEnumerator())
{
if (iterator.MoveNext())
{
firstAction(iterator.Current);
}
while (iterator.MoveNext())
{
subsequentActions(iterator.Current);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是相对简单的...给出一个特殊的最后一个动作稍微有点困难:
public static void IterateWithSpecialLast<T>(this IEnumerable<T> source,
Action<T> allButLastAction,
Action<T> lastAction)
{
using (IEnumerator<T> iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
return;
}
T previous = iterator.Current;
while (iterator.MoveNext())
{
allButLastAction(previous);
previous = iterator.Current;
}
lastAction(previous);
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:由于您的评论与这个性能来讲,我要重申我在这个答案的评论:虽然这普遍的问题是相当普遍的,它不是普遍的情况是这样的性能瓶颈,这是值得的微优化左右.实际上,我不记得曾经遇到过循环机械成为瓶颈的情况.我敢肯定它会发生,但是这是不是"普通".如果我碰到这个问题,我会特殊情况特定的代码,最好的解决方案将取决于究竟哪些代码需要做的.
但是,一般而言,我认为可读性和可重用性远远超过微优化.
Mar*_*ers 11
对于您的具体示例,有一个标准的解决方案:string.Join.这样可以正确添加分隔符,这样您就不必自己编写循环.
如果你真的想自己写一个你可以使用的方法如下:
string delimiter = "";
foreach (var element in array)
{
append(delimiter);
append(element);
delimiter = ",";
}
Run Code Online (Sandbox Code Playgroud)
这应该是合理有效的,我认为这是合理的阅读.常量字符串","被实现,因此不会导致在每次迭代时创建新字符串.当然,如果性能对您的应用程序至关重要,那么您应该进行基准而非猜测
你已经愿意放弃foreach了.所以这应该是合适的:
using (var enumerator = array.GetEnumerator()) {
if (enumerator.MoveNext()) {
for (;;) {
append(enumerator.Current);
if (!enumerator.MoveNext()) break;
append(delimiter);
}
}
}
Run Code Online (Sandbox Code Playgroud)
你当然可以goto在C#中创建一个解决方案(注意:我没有添加null检查):
string Join(string[] array, string delimiter) {
var sb = new StringBuilder();
var enumerator = array.GetEnumerator();
if (enumerator.MoveNext()) {
goto start;
loop:
sb.Append(delimiter);
start: sb.Append(enumerator.Current);
if (enumerator.MoveNext()) goto loop;
}
return sb.ToString();
}
Run Code Online (Sandbox Code Playgroud)
对于您的具体示例,这对我来说非常简单(这是您描述的解决方案之一):
string Join(string[] array, string delimiter) {
var sb = new StringBuilder();
foreach (string element in array) {
sb.Append(element);
sb.Append(delimiter);
}
if (sb.Length >= delimiter.Length) sb.Length -= delimiter.Length;
return sb.ToString();
}
Run Code Online (Sandbox Code Playgroud)
如果你想要功能,你可以尝试使用这种折叠方法:
string Join(string[] array, string delimiter) {
return array.Aggregate((left, right) => left + delimiter + right);
}
Run Code Online (Sandbox Code Playgroud)
虽然它读起来真的很好,但它没有使用StringBuilder,所以你可能想要滥用Aggregate一点来使用它:
string Join(string[] array, string delimiter) {
var sb = new StringBuilder();
array.Aggregate((left, right) => {
sb.Append(left).Append(delimiter).Append(right);
return "";
});
return sb.ToString();
}
Run Code Online (Sandbox Code Playgroud)
或者你可以使用它(从这里借用其他答案的想法):
string Join(string[] array, string delimiter) {
return array.
Skip(1).
Aggregate(new StringBuilder(array.FirstOrDefault()),
(acc, s) => acc.Append(delimiter).Append(s)).
ToString();
}
Run Code Online (Sandbox Code Playgroud)