我正在玩AST
,我想实现一个遍历我的树并返回值的类Visitor
。
我以这种方式尝试过,但是我的代码无法编译:
abstract class ASTVisitor<T> {
public abstract T Visit(SumExpr e);
public abstract T Visit(ProductExpr e);
public abstract T Visit(ConstantExpr e);
public abstract T Visit(SymbolExpr e);
}
class DerivateVisitor : ASTVisitor<ASTExpr> {
public override ASTExpr Visit(SumExpr e){
return new SumExpr(
Visit(e.A),
Visit(e.B));
}
public override ASTExpr Visit(ProductExpr e){
return new SumExpr(
new ProductExpr(Visit(e.A), e.B),
new ProductExpr(Visit(e.B), e.A));
}
public override ASTExpr Visit(ConstantExpr e){
return new ConstantExpr(0);
}
public override ASTExpr Visit(SymbolExpr e) {
return new ConstantExpr(1);
}
}
SumExpr
,ProductExpr
,ConstantExpr
,SymbolExpr
实现:
class ASTExpr{}
class ProductExpr : ASTExpr{
public ASTExpr A, B;
public ProductExpr(ASTExpr a, ASTExpr b) =>
(A, B) = (a, b);
public override string ToString() => $"({A.ToString()}) * ({B.ToString()})";
}
class ConstantExpr : ASTExpr {
public double Value;
public ConstantExpr(double v) => Value = v;
public override string ToString() => Value.ToString();
}
class SymbolExpr : ASTExpr {
public string Name;
private static Dictionary<string, SymbolExpr> Symbols = new Dictionary<string, SymbolExpr>();
private SymbolExpr(string v) {
Name = v;
}
public static SymbolExpr Create(string Name){
if(Symbols.ContainsKey(Name)) return Symbols[Name];
return Symbols[Name] = new SymbolExpr(Name);
}
public override string ToString() => $"Symbol({Name})";
}
class SumExpr : ASTExpr{
public ASTExpr A, B;
public SumExpr(ASTExpr a, ASTExpr b) =>
(A, B) = (a, b);
public override string ToString() => $"({A.ToString()}) + ({B.ToString()})";
}
SumExpr
,ProductExpr
,ConstantExpr
,SymbolExpr
继承自ASTExpr
。
为什么这不起作用?以及如何获得这种行为?
这是编译错误:
exit status 1
main.cs(56,7): error CS1502: The best overloaded method match for `ASTVisitor<ASTExpr>.Visit(SumExpr)' has some invalid arguments
main.cs(45,21): (Location of the symbol related to previous error)
main.cs(56,13): error CS1503: Argument `#1' cannot convert `ASTExpr' expression to type `SumExpr'
main.cs(57,7): error CS1502: The best overloaded method match for `ASTVisitor<ASTExpr>.Visit(SumExpr)' has some invalid arguments
main.cs(45,21): (Location of the symbol related to previous error)
main.cs(57,15): error CS1503: Argument `#1' cannot convert `ASTExpr' expression to type `SumExpr'
main.cs(61,23): error CS1502: The best overloaded method match for `ASTVisitor<ASTExpr>.Visit(SumExpr)' has some invalid arguments
main.cs(45,21): (Location of the symbol related to previous error)
main.cs(61,31): error CS1503: Argument `#1' cannot convert `ASTExpr' expression to type `SumExpr'
main.cs(62,23): error CS1502: The best overloaded method match for `ASTVisitor<ASTExpr>.Visit(SumExpr)' has some invalid arguments
main.cs(45,21): (Location of the symbol related to previous error)
main.cs(62,31): error CS1503: Argument `#1' cannot convert `ASTExpr' expression to type `SumExpr'
Compilation failed: 8 error(s), 0 warnings
谢谢。
答案 0 :(得分:1)
问题是
class SumExpr : ASTExpr
{
public ASTExpr A, B;
public SumExpr(ASTExpr a, ASTExpr b) =>
(A, B) = (a, b);
public override string ToString() => $"({A.ToString()}) + ({B.ToString()})";
}
和
public override ASTExpr Visit(SumExpr e)
{
return new SumExpr(
Visit(e.A),
Visit(e.B));
}
e.A是ASTExpr,但是没有可以为ASTExpr调用的访问方法。
对象类型是在编译时而不是在运行时定义的。
添加以下方法
public override ASTExpr Visit(ASTExpr e)
{
if (e as SumExpr != null)
return Visit(e as SumExpr);
if (e as ProductExpr != null)
return Visit(e as ProductExpr);
if (e as ConstantExpr != null)
return Visit(e as ConstantExpr);
if (e as SymbolExpr != null)
return Visit(e as SymbolExpr);
throw new ArgumentException();
}
答案 1 :(得分:0)
在代码的这一部分中,您正在使用无效的参数(e.A)调用ASTVisitor<ASTExpr>.Visit
,如预期的那样(e)。
Visit
方法期望其中一个对象(SumExpr , ProductExpr, ConstantExpr, SymbolExpr
)
并且您正在传递e.A
ASTExpr
public override ASTExpr Visit(SumExpr e){
return new SumExpr(
Visit(e.A), // You are passing ASTExpr where as SumExpr is expected
Visit(e.B)); // You are passing ASTExpr where as SumExpr is expected
}
这肯定会改变您必须处理的逻辑。
希望这会有所帮助
答案 2 :(得分:0)
我建议对@Sergey Prosin的回答稍作改进。使用as
可以,但是比较慢,因为每个使用者都会先检查类型是否匹配。相反,您可以将as
表达式结果分配给变量,然后使用它,或者使用新的is
模式匹配语法:
public override ASTExpr Visit(ASTExpr e)
{
if (e is SumExpr sum)
return Visit(sum);
if (e is ProductExpr product)
return Visit(product);
if (e is ConstantExpr constant)
return Visit(constant);
if (e is SymbolExpr symbol)
return Visit(symbol);
throw new ArgumentException();
}
或更凉爽,即switch
模式匹配:
public override ASTExpr Visit(ASTExpr e)
{
switch (e)
{
case SumExpr sum:
return Visit(sum);
case ProductExpr product:
return Visit(product);
case ConstantExpr constant:
return Visit(constant);
case SymbolExpr symbol:
return Visit(symbol);
default:
throw new ArgumentException();
}
}