0

我遇到了一个非常奇怪的问题。因此,背景是我们在 WordContentControl和自定义对象之间建立了映射,我们使用该对象来存储与该控件内的内容相关的一些信息。我们使用 aSortedList<ContentControl, OurCustomObject>来维护这个映射。SortedList 部分对于能够找到下一个/上一个内容控件以及能够快速访问与内容控件关联的对象很有用。

为了进行设置,我们执行以下操作:

var dictOfObjs = Globals.ThisAddIn.Application.ActiveDocument.ContentControls
    .Cast<ContentControl>()
    .ToDictionary(key => key, elem => new OurCustomObject(elem));
var comparer = Comparer<ContentControl>
    .Create((x, y) => x.Range.Start.CompareTo(y.Range.Start));
var list = new SortedList<ContentControl, OurCustomObject>(dictOfObjs, storedcomparer);

这似乎工作得很好,但我最近在一个包含约 5000 个内容控件的文档上尝试了它,它减慢到绝对爬行(实例化 SortedList 3 分钟以上)。

所以这已经够奇怪了,但更多的奇怪还在后面。我添加了一些日志记录来弄清楚发生了什么,ContentControl并发现在列表中使用它们之前记录每个的开始可以加快大约 60 倍。(是的,添加日志记录加快了速度!)。这是更快的代码:

var dictOfObjs = Globals.ThisAddIn.Application.ActiveDocument.ContentControls
    .Cast<ContentControl>()
    .ToDictionary(key => key, elem => new OurCustomObject(elem));

foreach (var pair in dictOfObjs)
{
    _logger.Debug("Start: " + pair.Key.Range.Start);
}

var comparer = Comparer<ContentControl>
    .Create((x, y) => x.Range.Start.CompareTo(y.Range.Start));
var list = new SortedList<ContentControl, OurCustomObject>(dictOfObjs, storedcomparer);

SortedList 的构造函数调用Array.Sort<TKey, TValue>(keys, values, comparer);字典的键和值。我不明白为什么事先在循环中访问 Range 对象会加快速度。也许与访问它们的顺序有关?foreach 循环将按照它们在文档中出现的顺序访问它们,而 Array.Sort 将四处跳跃。

编辑:当我说 SortedList 时,我的意思是System.Collections.Generic.SortedList<TKey, TValue>. 这是我正在使用的构造函数的代码:

public SortedList(IDictionary<TKey, TValue> dictionary, IComparer<TKey> comparer) 
    : this((dictionary != null ? dictionary.Count : 0), comparer) {
    if (dictionary==null)
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);

    dictionary.Keys.CopyTo(keys, 0);
    dictionary.Values.CopyTo(values, 0);
    Array.Sort<TKey, TValue>(keys, values, comparer);
    _size = dictionary.Count;            
}
4

1 回答 1

0

除了您的性能问题之外,我认为您的解决方案(除非文档实际上是静态文档)会随着时间的推移而失败。Range.Start 位置往往会根据在文档中添加/删除内容而发生变化。

为了证明这一点,添加以下小宏并从 VBA 运行它:

Sub testccstart()

    Dim cc As ContentControl
    Set cc = ActiveDocument.ContentControls.Add(wdContentControlRichText)

    MsgBox cc.Range.Start

    ActiveDocument.Range(0).InsertBefore "Blablabla"

    MsgBox cc.Range.Start

End Sub

您会注意到,在您在文档开头输入文本的那一刻,内容控件的 Range.Start 从 1 变为 10。因此,文档中的所有编辑都要求您重新加载基于 Range.Start 的列表。

不过,您的问题的答案是,通过添加日志记录,您触发了一些他们所谓的“延迟加载”(仅在实际访问时加载内容)。Office 中的各种优化并不总是很清楚(例如,使用数组访问 Excel 范围通常要快得多)。

我进行了一些测试,想知道这是否适合您:

 var dictOfObjs = document.ContentControls
                        .Cast<ContentControl>()
                        .ToDictionary(key => key.Range.Start, elem => new OurCustomObject(elem));

 var comparer = Comparer<int>.Create((x, y) => x.CompareTo(y));

 var list = new SortedList<int, OurCustomObject>(dictOfObjs, comparer);

而不是使用内容控件作为键(我猜你已经将内容控件存储在 OurCustomObject 中?)假设每个内容控件都有一个唯一的开始位置,使用开始位置作为键,这使我对 1600 个项目的处理从 48 回到 5秒...

于 2016-04-05T10:57:53.320 回答