C#-基于参数类型的不同功能覆盖

时间:2018-08-01 14:30:07

标签: c# inheritance methods overloading

我正在玩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);
  }
}

SumExprProductExprConstantExprSymbolExpr实现:

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()})";
}

SumExprProductExprConstantExprSymbolExpr继承自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

谢谢。

3 个答案:

答案 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();
    }
}