代码之家  ›  专栏  ›  技术社区  ›  Phil Booth

我应该如何重构一个长的尝试链并捕获包装好的推测性铸造操作

  •  3
  • Phil Booth  · 技术社区  · 16 年前

    我有一些C代码,它使用.NET框架中的xml.schema类来遍历XML模式。各种简单的类型限制在框架中抽象为一组从xml.schema.xmlschemafacet派生的类。除非我遗漏了一些东西,否则了解给定方面的派生方面类型的唯一方法是推测性地将其强制转换到其中一个方面,以在失败时捕获结果无效的强制转换操作。这样做会给我留下一个非常难看的功能:

    private void NavigateFacet(XmlSchemaFacet facet)
    {
        try
        {
            handler.Length((XmlSchemaLengthFacet)facet);
        }
        catch(InvalidCastException)
        {
            try
            {
                handler.MinLength((XmlSchemaMinLengthFacet)facet);
            }
            catch(InvalidCastException)
            {
                try
                {
                    handler.MaxLength((XmlSchemaMaxLengthFacet)facet);
                }
                catch(InvalidCastException)
                {
                    ...
                }
            }
        }
    }
    

    我认为必须有更优雅的方法来实现这一点;要么使用一些我从.NET框架中遗漏的属性,要么使用一些巧妙的OO技巧。有人能启发我吗?

    6 回复  |  直到 16 年前
        1
  •  7
  •   Robert Rossney    16 年前

    因为比起调试代码,我更喜欢调试数据,所以我会这样做,特别是当代码必须处理所有 XmlSchemaFacet 子类:

    Dictionary<Type, Action<XmlSchemaFacet>> HandlerMap = 
       new Dictionary<Type, Action<XmlSchemaFacet>>
    {
       {typeof(XmlSchemaLengthFacet), handler.Length},
       {typeof(XmlSchemaMinLengthFacet), handler.MinLength},
       {typeof(XmlSchemaMaxLengthFacet), handler.MaxLength}
    };
    
    HandlerMap[facet.GetType()](facet);
    

    这会抛出一个 KeyNotFoundException 如果 facet 不是已知类型。注意,所有处理程序方法都必须从 XMLStudiaFa刻板 ,所以您可能不会在代码的总行数上进行保存,但您肯定会在代码的路径数上进行保存。

    还有一点是(假设映射是预构建的)使用字典将类型映射到方法比遍历线性类型列表更快,这本质上就是使用一组 if 积木让你兴奋。

        2
  •  6
  •   Community Mohan Dere    9 年前

    您可以尝试使用 as 关键字。有些人建议使用 is 关键字。我发现 this 解释为什么 作为 更好。

    一些示例代码:

    private void NavigateFacet(XmlSchemaFacet facet)
    {
      XmlSchemaLengthFacet lengthFacet = facet as XmlSchemaLengthFacet;
      if (lengthFacet != null)
      {
        handler.Length(lengthFacet);
      }
      else
      {
        // Re-try with XmlSchemaMinLengthFacet, etc.
      }
    }
    
        3
  •  4
  •   MusiGenesis    16 年前
    private void NavigateFacet(XmlSchemaFacet facet)
    {
        if (facet is XmlSchemaLengthFacet)
        {
            handler.Length((XmlSchemaLengthFacet)facet);
        }
        else if (facet is XmlSchemaMinLengthFacet)
        {
            handler.MinLength((XmlSchemaMinLengthFacet)facet);
        }
        else if (facet is XmlSchemaMaxLengthFacet)
        {
            handler.MinLength((XmlSchemaMaxLengthFacet)facet);
        }
    
        // etc.
    }
    

    更新 :我决定对这里讨论的不同方法进行基准测试( is as )下面是我使用的代码:

    object c1 = new Class1();
    int trials = 10000000;
    Class1 tester;
    Stopwatch watch = Stopwatch.StartNew();
    for (int i = 0; i < trials; i++)
    {
        if (c1 is Class1)
        {
            tester = (Class1)c1;
        }
    }
    watch.Stop(); 
    MessageBox.Show(watch.ElapsedMilliseconds.ToString()); // ~104 ms 
    watch.Reset();
    watch.Start();
    for (int i = 0; i < trials; i++)
    {
        tester = c1 as Class1;
        if (tester != null)
        {
            // 
        }
    }
    watch.Stop(); 
    MessageBox.Show(watch.ElapsedMilliseconds.ToString()); // ~86 ms
    watch.Reset();
    watch.Start();
    for (int i = 0; i < trials; i++)
    {
        if (c1 is Class1)
        {
            // 
        }
    }
    watch.Stop();     
    MessageBox.Show(watch.ElapsedMilliseconds.ToString()); // ~74 ms
    watch.Reset();
    watch.Start();
    for (int i = 0; i < trials; i++)
    {
        //
    }
    watch.Stop();     
    MessageBox.Show(watch.ElapsedMilliseconds.ToString()); // ~50 ms
    

    如预期,使用 作为 关键字,然后检查是否为空 比使用 关键字和强制转换(36毫秒对54毫秒,减去循环本身的成本)。

    然而 ,使用 关键字,然后 铸造速度更快(24ms),这意味着用一系列 检查,然后在识别正确类型时仅铸造一次 能够 实际上更快(取决于在识别正确的类型之前必须在此方法中完成的不同类型检查的数量)。

    然而,更深的一点是,这次试验的试验次数是10次。 百万, 这意味着你使用哪种方法真的没什么区别。使用 当使用 作为 检查空值需要0.0000036毫秒(在我的古代笔记本上)。

        4
  •  3
  •   Community Mohan Dere    9 年前

    您可以尝试使用 as 关键字-如果强制转换失败,您将获得 null 而不是例外。

    private void NavigateFacet(XmlSchemaFacet facet)
    {
        var length = facet as XmlSchemaLengthFacet;
        if (length != null)
        {
            handler.Length(length);
            return;
        }
    
        var minlength = facet as XmlSchemaMinLengthFacet;
        if (minlength != null)
        {
            handler.MinLength(minlength);
            return;
        }
    
        var maxlength = facet as XmlSchemaMaxLengthFacet;
        if (maxlength != null)
        {
            handler.MaxLength(maxlength);
            return;
        }
        ...
    }
    

    如果你能控制这些类,我建议使用访问者模式的变体(aka Double Despatch 为了更清晰地恢复类型信息,但由于没有,这是一种相对简单的方法。

    更新 :使用变量存储 作为 CAST避免了两次类型检查逻辑。

    更新2 :当C 4可用时,您可以使用 dynamic 为您调度:

    public class HandlerDemo 
    {
        public void Handle(XmlSchemaLengthFacet facet) { ... }
        public void Handle(XmlSchemaMinLengthFacet facet) { ... }
        public void Handle(XmlSchemaMaxLengthFacet facet) { ... }
        ...
    }
    
    private void NavigateFacet(XmlSchemaFacet facet)
    {
        dynamic handler = new HandlerDemo();
        handler.Handle(facet);
    }
    

    这将起作用,因为动态对象上的方法分派使用与普通方法重写相同的规则,但在运行时而不是编译时进行计算。

    在这种情况下,动态语言运行库(DLR)将执行与本文(和其他答案)中所示代码大致相同的技巧,但同时增加了性能缓存。

        5
  •  1
  •   ChaosPandion    16 年前

    这是一种不需要任何尝试捕获的正确方法。

    if (facet is XmlSchemaLengthFacet)
    {
        handler.Length((XmlSchemaLengthFacet)facet); 
    } 
    else if (facet is XmlSchemaMinLengthFacet)
    {
        handler.MinLength((XmlSchemaMinLengthFacet)facet); 
    } 
    else if (facet is XmlSchemaMaxLengthFacet)
    {
        handler.MaxLength((XmlSchemaMaxLengthFacet)facet); 
    } 
    else
    {
        //Handle Error
    }
    
        6
  •  0
  •   codymanix    16 年前
    • 使用“is”确定对象是否属于给定类型

    • 使用“as”进行类型转换,它比正常的强制转换快,不会引发异常,而是在错误时重新返回空值。

    你可以这样做:

    private void NavigateFacet(XmlSchemaFacet facet)
    {
      if (facet is XmlSchemaLengthFacet)
      {
            handler.Length(facet as XmlSchemaLengthFacet);
      }
      else if (facet is XmlSchemaMinLengthFacet)
      {
            handler.MinLength(facet as XmlSchemaMinLengthFacet);
      }
      else if (facet is XmlSchemaMaxLengthFacet)
      {
           handler.MaxLength(facet as XmlSchemaMaxLengthFacet);
      }
    }
    
    推荐文章