问题
我正在使用一个已经存在的库,但我无法访问其源代码。这个库代表了一个AST。
我想复制这个AST的部分,并在此过程中重命名变量的引用。由于可能会有一个包含Expression对象的AssignCommand-Object,我希望能够通过自己的函数递归地调用每个对象进行复制。然而,由于我无法访问库的代码,因此我无法添加像CopyAndRename(string prefix)
这样的方法。
因此,我的方法是创建一个单一的函数Rename
,并提供多个重载。这样,我将拥有以下一组函数:
public static Command Rename(Command cmd, string prefix)
public static AssignCommand Rename(AssignCommand cmd, string prefix)
public static AdditionExpressionRename(AdditionExpression expr, string prefix)
....
现在一个函数由List<Command>
组成,其中AssignCommand
是Command
的子类。我以为只需将Command
传递给Rename
函数,运行时就会找到最具体的函数。然而,情况并非如此,所有命令都被传递到Command Rename(Command cmd, string prefix)
。为什么会这样?有没有一种方法可以委托调用正确的函数而不使用丑陋的is
操作?
最小示例
我已将此问题分解为以下NUnit测试代码:
using NUnit.Framework;
public class TopClass{
public int retVal;
}
public class SubClassA : TopClass{ }
[TestFixture]
public class ThrowawayTest {
private TopClass Foo (TopClass x) {
x.retVal = 1;
return x;
}
private SubClassA Foo (SubClassA x) {
x.retVal = 2;
return x;
}
[Test]
public void OverloadTest(){
TopClass t = new TopClass();
TopClass t1 = new SubClassA();
SubClassA s1 = new SubClassA();
t = Foo (t);
t1 = Foo (t1);
s1 = Foo (s1);
Assert.AreEqual(1, t.retVal);
Assert.AreEqual(2, s1.retVal);
Assert.AreEqual(2, t1.retVal);
}
}
我的问题可以简化为:“如何在不使用is检查的情况下,以优雅、多态和面向对象的方式修复上述测试?”
扩展方法
我还尝试过使用扩展方法,代码如下。然而,这并没有解决问题,因为它们只是上述方法的语法糖:using NUnit.Framework;
using ExtensionMethods;
public class TopClass{
public int retVal;
}
public class SubClassA : TopClass{ }
[TestFixture]
public class ThrowawayTest {
private TopClass Foo (TopClass x) {
x.retVal = 1;
return x;
}
private SubClassA Foo (SubClassA x) {
x.retVal = 2;
return x;
}
[Test]
public void OverloadTest(){
TopClass t = new TopClass();
TopClass t1 = new SubClassA();
SubClassA s1 = new SubClassA();
t.Foo(); s1.Foo(); t1.Foo();
Assert.AreEqual(1, t.retVal);
Assert.AreEqual(2, s1.retVal);
Assert.AreEqual(2, t1.retVal);
}
}
namespace ExtensionMethods{
public static class Extensions {
public static void Foo (this TopClass x) {
x.retVal = 1;
}
public static void Foo (this SubClassA x) {
x.retVal = 2;
}
}
}