0

非常に奇妙な問題が発生しています。したがって、その背景には、WordContentControlと、そのコントロール内のコンテンツに関連する情報を格納するために使用するカスタム オブジェクトとの間のマッピングがあります。SortedList<ContentControl, OurCustomObject>このマッピングを維持するためにa を使用します。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 に答える