Visual Studio 或其他工具能否按行为比较两段代码?
Can Visual Studio, or another Tool, compare two pieces of code by behaviour?
在编程时,即使不总是,也经常有多种方法来获得完全相同的行为。
我知道有很多工具允许逐行比较代码文件。我正在寻找的是一种可以在比较行为时提供帮助或完成全部工作的工具。
我很清楚在大多数情况下,在执行期间的某个时刻,副作用、内存状态、堆栈跟踪和许多其他事情会有所不同。我真的在寻找一个不会考虑所有这些的顶级比较。就像文字比较工具可以轻松忽略空行和注释。
这是一个例子:
1:
static void main()
{
string i = "Hello World!";
Console.Write(i);
}
2:
static void main()
{
string helloWorldString = "Hello World!";
Console.Write(helloWorldString);
}
3:
static void main()
{
string myString = "Hello World!";
WriteToConsole(myString);
}
static void WriteToConsole(string text)
{
Console.Write(text);
}
这 3 个简单的 Hello World 程序示例 应该 表现相同,至少从用户的角度来看,不包括极端系统配置边缘情况。我希望同一台机器在所有三种情况下都能做完全相同的事情。
当然,对于计算机来说,这看起来是一项很难快速完成的任务,至少从我的角度来看是这样。我不希望这样的工具是完美的,尤其是当项目有多个类、表单、全局变量等时
但是在进行一些重构时,像 "With your changes, a behaviour has changed even though you hadn't noticed" 或 "The behaviour before and after your change stayed the same" 这样的快速提醒会大大节省时间。
Visual Studio、Resharper 或其他地方是否有这样的工具?
作为额外练习,因为我很好奇,如果 C# 不存在,其他语言是否存在?是否有正在进行的已知项目?
我相信您正在寻找的是单元测试。它们允许您测试某些函数的结果而不必担心实现。看这里:
这是不可能的。一个问题是@BJMyers 在他们的评论中已经提到的停止问题。但另一个障碍是,即使是你的小片段实际上也不会做同样的事情。让我们看看他们生产的IL。 #1 的第一个:
IL_0000: nop
IL_0001: ldstr "Hello World!"
IL_0006: stloc.0 // i
IL_0007: ldloc.0 // i
IL_0008: call System.Console.Write
IL_000D: nop
IL_000E: ret
对于#2:
IL_0000: nop
IL_0001: ldstr "Hello World!"
IL_0006: stloc.0 // helloWorldString
IL_0007: ldloc.0 // helloWorldString
IL_0008: call System.Console.Write
IL_000D: nop
IL_000E: ret
好吧,它们是相同的 - 这并不奇怪,因为唯一的区别是编译代码中不再存在的变量名。但现在#3:
IL_0000: nop
IL_0001: ldstr "Hello World!"
IL_0006: stloc.0 // myString
IL_0007: ldloc.0 // myString
IL_0008: call UserQuery.WriteToConsole
IL_000D: nop
IL_000E: ret
WriteToConsole:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call System.Console.Write
IL_0007: nop
IL_0008: ret
如您所见,它还有 3 条指令(此处不计算 nop
)。因此,您的工具需要检查 IL 并确定哪个输入产生哪个结果。这将引导我们回到停机问题。当然,静态分析器可以识别一些 模式,但不可能有通用的解决方案。
如果您的目标是确保重构不会破坏任何东西,那么您能做的最好的就是单元测试,就像@dktaylor 在他们的回答中已经建议的那样。
通常,您无法静态分析代码行为,因为它可能取决于文件和用户输入等环境。
您可以 运行 一个程序,并使用我的 Runtime Flow 工具将行为捕获为一系列带有参数和 return 值的方法调用:
void Program.Main([])
void Program.WriteToConsole("Hello World!")
void Console.Write("Hello World!")
>SyncTextWriter Console.get_Out()
void SyncTextWriter.Write("Hello World!")
void StreamWriter.Write("Hello World!")
void StreamWriter.CheckAsyncTaskInProgress()
>void String.CopyTo(0, [�, �, �, �, �, �, �, �, �, ...246..., �], 0, 12)
void StreamWriter.Flush(true, false)
>12 EncoderNLS.GetBytes([H, e, l, l, o, , W, o, r, ...246..., �], 0, 12, [0, 0, 0, 0, 0, 0, 0, 0, 0, ...247..., 0], 0, false)
>void __ConsoleStream.Write([72, 101, 108, 108, 111, 32, 87, 111, 114, ...247..., 0], 0, 12)
>void __ConsoleStream.Flush()
但是,如果您的程序生成图形输出或更改数据库,则您需要其他工具。
顺便说一句,使用自动化工具执行重构肯定会保留程序行为。
在编程时,即使不总是,也经常有多种方法来获得完全相同的行为。
我知道有很多工具允许逐行比较代码文件。我正在寻找的是一种可以在比较行为时提供帮助或完成全部工作的工具。
我很清楚在大多数情况下,在执行期间的某个时刻,副作用、内存状态、堆栈跟踪和许多其他事情会有所不同。我真的在寻找一个不会考虑所有这些的顶级比较。就像文字比较工具可以轻松忽略空行和注释。
这是一个例子:
1:
static void main()
{
string i = "Hello World!";
Console.Write(i);
}
2:
static void main()
{
string helloWorldString = "Hello World!";
Console.Write(helloWorldString);
}
3:
static void main()
{
string myString = "Hello World!";
WriteToConsole(myString);
}
static void WriteToConsole(string text)
{
Console.Write(text);
}
这 3 个简单的 Hello World 程序示例 应该 表现相同,至少从用户的角度来看,不包括极端系统配置边缘情况。我希望同一台机器在所有三种情况下都能做完全相同的事情。
当然,对于计算机来说,这看起来是一项很难快速完成的任务,至少从我的角度来看是这样。我不希望这样的工具是完美的,尤其是当项目有多个类、表单、全局变量等时
但是在进行一些重构时,像 "With your changes, a behaviour has changed even though you hadn't noticed" 或 "The behaviour before and after your change stayed the same" 这样的快速提醒会大大节省时间。
Visual Studio、Resharper 或其他地方是否有这样的工具?
作为额外练习,因为我很好奇,如果 C# 不存在,其他语言是否存在?是否有正在进行的已知项目?
我相信您正在寻找的是单元测试。它们允许您测试某些函数的结果而不必担心实现。看这里:
这是不可能的。一个问题是@BJMyers 在他们的评论中已经提到的停止问题。但另一个障碍是,即使是你的小片段实际上也不会做同样的事情。让我们看看他们生产的IL。 #1 的第一个:
IL_0000: nop
IL_0001: ldstr "Hello World!"
IL_0006: stloc.0 // i
IL_0007: ldloc.0 // i
IL_0008: call System.Console.Write
IL_000D: nop
IL_000E: ret
对于#2:
IL_0000: nop
IL_0001: ldstr "Hello World!"
IL_0006: stloc.0 // helloWorldString
IL_0007: ldloc.0 // helloWorldString
IL_0008: call System.Console.Write
IL_000D: nop
IL_000E: ret
好吧,它们是相同的 - 这并不奇怪,因为唯一的区别是编译代码中不再存在的变量名。但现在#3:
IL_0000: nop
IL_0001: ldstr "Hello World!"
IL_0006: stloc.0 // myString
IL_0007: ldloc.0 // myString
IL_0008: call UserQuery.WriteToConsole
IL_000D: nop
IL_000E: ret
WriteToConsole:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call System.Console.Write
IL_0007: nop
IL_0008: ret
如您所见,它还有 3 条指令(此处不计算 nop
)。因此,您的工具需要检查 IL 并确定哪个输入产生哪个结果。这将引导我们回到停机问题。当然,静态分析器可以识别一些 模式,但不可能有通用的解决方案。
如果您的目标是确保重构不会破坏任何东西,那么您能做的最好的就是单元测试,就像@dktaylor 在他们的回答中已经建议的那样。
通常,您无法静态分析代码行为,因为它可能取决于文件和用户输入等环境。
您可以 运行 一个程序,并使用我的 Runtime Flow 工具将行为捕获为一系列带有参数和 return 值的方法调用:
void Program.Main([])
void Program.WriteToConsole("Hello World!")
void Console.Write("Hello World!")
>SyncTextWriter Console.get_Out()
void SyncTextWriter.Write("Hello World!")
void StreamWriter.Write("Hello World!")
void StreamWriter.CheckAsyncTaskInProgress()
>void String.CopyTo(0, [�, �, �, �, �, �, �, �, �, ...246..., �], 0, 12)
void StreamWriter.Flush(true, false)
>12 EncoderNLS.GetBytes([H, e, l, l, o, , W, o, r, ...246..., �], 0, 12, [0, 0, 0, 0, 0, 0, 0, 0, 0, ...247..., 0], 0, false)
>void __ConsoleStream.Write([72, 101, 108, 108, 111, 32, 87, 111, 114, ...247..., 0], 0, 12)
>void __ConsoleStream.Flush()
但是,如果您的程序生成图形输出或更改数据库,则您需要其他工具。
顺便说一句,使用自动化工具执行重构肯定会保留程序行为。