【问题标题】:Are there any tools that can help us refactor IEnumerator properties to IList<T> or similar?是否有任何工具可以帮助我们将 IEnumerator 属性重构为 IList<T> 或类似的?
【发布时间】:2012-10-19 20:33:23
【问题描述】:

我们有一个非常古老的代码库(实际上质量并不差)。它可以追溯到 .Net 预发布的时候,我怀疑这是造成这些奇怪约定的原因。

无论如何,我们刚刚开始放弃对 .Net 1.1 的支持,并且正在将事物转换为泛型并使用 Linq 和所有有趣的东西。我们代码库中最烦人的模式之一是我们会有类似

private ArrayList mylist;
public IEnumerator MyList
{
  get
  {
    if(mylist==null)
      return new EmptyEnumerator.Enumerator;
    return mylist.GetEnumerator();
  }
}

这种模式特别可怕,因为它阻止我们简单地执行foreach(var item in MyList),因为 IEnumerator 没有实现 IEnumerable。相反,我们必须这样做:

IEnumerator enumerator=MyList;
while(enumerator.MoveNext())
{
    object item=enumerator.Current;
}

所以,对于重构,我们当然希望使用 ReadOnlyCollection&lt;T&gt;IList&lt;T&gt; 或类似的东西。但是,要做到这一点,我们必须更新对MyList 的每个引用:

IEnumerator enumerator=MyList;

IEnumerator enumerator=MyList.GetEnumerator();

在某些情况下,我们可以对一个属性有超过一百个引用。是否有任何工具可以使这更容易?我们最近得到了 Resharper(不是为了这个问题,只是为了一般用途),但它似乎没有涵盖这种类型的场景。

【问题讨论】:

  • 为什么不修复用途 - 而不是继续手动调用GetEnumerator()?我希望它们中的大多数最终都会成为foreach 循环,对吧?
  • 用法是否足够一致,您可以正则表达式或编写快速脚本来读取所有 .cs 文件并查找/替换模式?
  • @JonSkeet 我们很乐意,但我们没有一个月的时间来完成这项任务 :) 所以我们现在的计划是现在解决根本原因,这样我们就可以编写好的新代码,并在我们遇到旧代码时对其进行修复
  • 螺丝自动化,找个实习生就行了。
  • @Earlz 您能否拥有两个属性,一个是 IEnumerator(现有的),另一个是名称相似的 IEnumerable&lt;T&gt;。新用途使用新特性,旧用途使用旧特性。当你遇到旧的时重构它的用途,然后尽可能将其完全删除。

标签: c# .net refactoring ienumerable automated-refactoring


【解决方案1】:

听起来你需要做的是返回一个实现IEnumeratorIEnumerable&lt;T&gt;的类

制作你自己的类型实际上并不难:

public class MessedUpIterator<T> : IEnumerable<T>, IEnumerator
{
    private IEnumerable<T> source;
    private IEnumerator enumerator;

    private IEnumerator MyEnumerator
    {
        get
        {
            return enumerator ?? source.GetEnumerator();
        }
    }

    public MessedUpIterator(IEnumerable<T> source)
    {
        this.source = source;
    }
    public IEnumerator<T> GetEnumerator()
    {
        return source.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return source.GetEnumerator();
    }

    object IEnumerator.Current
    {
        get { return MyEnumerator.Current; }
    }

    bool IEnumerator.MoveNext()
    {
        return MyEnumerator.MoveNext();
    }

    void IEnumerator.Reset()
    {
        MyEnumerator.Reset();
    }
}

现在,您可以返回 both 的东西,而不是返回 任一 IEnumeratorIEnumerable&lt;T&gt;

请注意,IEnumerator 是显式实现的,而 IEnumerable&lt;T&gt; 是隐式实现的,因此它鼓励将其用作 IEnumerable,同时仍将其用作 IEnumerator 可能

是的,它很丑,但肯定会更糟。

【讨论】:

  • 我们正在努力使我们的代码变得更好,而不是更骇人听闻。此外,如果我们决定允许 IList 之类的东西,因为一段代码需要它的附加函数而不是 IEnumerable,那么这个解决方案将失败
  • @Earlz 创建一个实现IList&lt;T&gt;IEnumerator 的类很容易。由于IList&lt;T&gt; 扩展了IEnumerable&lt;T&gt;,这将是一个非常微不足道的变化;主要是为每个在内部存储的IList 上调用方法的IList 方法制作存根。至于它是 hackish,您通过指定不更改调用方代码排除了对问题的 实际 修复。
  • 好吧,调用者代码可以更改,只是不会大幅度更改。因此,我一直在寻找某种方法来自动将 .GetEnumerator() 附加到属性的每个引用
  • @Earlz 不过,这可能是一个很好的垫脚石。为您的关闭版本实施此操作,然后在所有正确完成后进行点发布。要么这样,要么你可能不得不在即将到来的周末求助于最基本的工具:血腥的指尖。
  • 克里斯说得对。您在短期内使用类似的方法,以便您能够对新代码使用正确的方法。然后,当您有可用资源时,进入并更改此类型以不实现IEnumerator。然后到处修复有语法错误的代码。当您用完时间时,请修复此类,以便所有内容都可以编译。全部修复后,您可以完全删除此类型,或者只是提交更改以不实现 IEnumerator
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-11
  • 2023-01-25
  • 2010-12-07
  • 1970-01-01
  • 2010-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多