【问题标题】:c# - different function override based on parameters' typec# - 基于参数类型的不同函数覆盖
【发布时间】:2018-08-01 14:30:07
【问题描述】:

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

提前致谢。

【问题讨论】:

  • 你能提供更多细节吗?你遇到了什么样的编译错误
  • 我添加了错误
  • 当您尝试编译该代码时,请在 Visual Studio 中包含错误列表中的消息。
  • 请提供 ASTExpr 和 SumExpr 定义。它们是如何连接的?
  • SumExpr、ProductExpr、ConstantExpr、SymbolExpr 和 ASTExpr 类是否也需要实现?

标签: c# inheritance methods overloading


【解决方案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 调用的方法 Visit。

对象的类型是在编译期间而不是在运行时定义的。

添加如下方法

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

【讨论】:

  • 但如果我为 ASTExpr 添加覆盖,所有对访问的调用都将引用该覆盖。对吗?
  • 不,编译器应该找到最合适的方法。您可以通过单击 VS 中的 GotoDefinition 上下文菜单项来检查
  • 您可以坚持github.com/Microsoft/referencesource/blob/master/System.Core/… 作为访问者实施的示例,并从那里获取实施细节。提供的代码看起来更直接,所以使用不同名称的 Visit 方法对我来说看起来更透明
【解决方案2】:

在您的这部分代码中,您调用 ASTVisitor&lt;ASTExpr&gt;.Visit 时使用了无效的参数 (e.A),正如它所期望的 (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
  }

这肯定会改变你必须处理的逻辑。

希望对你有帮助

【讨论】:

  • 我刚刚对参数进行了更改。将更新我的代码:)
【解决方案3】:

我建议对@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();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-04
    • 2013-09-11
    • 2012-05-20
    • 1970-01-01
    • 2011-10-24
    相关资源
    最近更新 更多