9

現在、ソリューション内の循環参照を確認したい場合は、 を選択しますArchitecture - Generate Dependency Graph - For Solution。次に、開いた新しいタブから を選択しますLayout - Analyzers - Circular References Analyzer。最後に、個々のアセンブリからドリルダウンして循環参照がある場合、それらがグラフ上で赤く強調表示され、エラー リストにも警告として表示されます。

私は同じクラスのメソッド間でも循環参照を見つけるつもりなので、これは非常にエラーが発生しやすく、適度に大きなコードベースでは時間がかかります。

ノードを展開せずにすべての警告を一度に取得する方法があるかどうか、または親ノードの強調表示をオンにして、確実に循環参照を含むアセンブルのみをドリルダウンできるようにする方法があるかどうかを知りたいです。

NDepend が役立つはずですが、私は物事をできるだけシンプルに保つことを好みます。そのため、追加のツールを採用することには常に注意を払っています。

4

1 に答える 1

7

はい、NDepend は循環参照を効率的に見つけることができます。あなたが考えるよりも簡単かもしれないので、その方法を説明させてください (免責事項: 私は NDepend の開発者の 1 人です)。これまでのところ、すぐに使える名前空間の依存関係サイクルを見つけることができますが、以下で説明しているように、名前空間内の型または型のメソッド間の循環も簡単に見つけることができます。

名前空間の依存関係のサイクルを一覧表示する既定の C# LINQ コード ルールがあります。このようなサイクルは、ディペンデンシー グラフまたはディペンデンシー マトリックスにエクスポートできます。これは、2012 年 6 月に Roslyn コード ベース CTP で実行されたルールのスクリーンショットです (実行に 16 ミリ秒しかかからなかったことに注意してください)。11 の異なるサイクルが見つかりました。スクリーンショットに示すように、各サイクルにドリルダウンして、サイクルをグラフにエクスポートできます。

名前空間の依存関係サイクルを一致させるルール

これは、7 つの名前空間サイクルの依存関係グラフです。従来の O リング サイクルよりも複雑に見えることに注意してください。ここで重要なのは、これらの名前空間のいずれかから、他のすべての名前空間にアクセスできることです。これは、サイクル (絡み合い) の一般化された概念です。

名前空間の依存関係サイクルの一致グラフ

名前空間の依存関係のサイクルを一覧表示する既定の C# LINQ コード ルールのコードは、一見すると難解に見えるかもしれません。しかし、C# 開発者は数分でそれを理解する必要があり、それを簡単に適応させてあらゆる種類の依存関係サイクルを見つけることができます。

たとえば、 (同じアセンブリの名前空間の代わりに)サイクルの同じ型のメソッドを見つけるには、すべての名前空間の単語をメソッドごとに、アセンブリの単語をごとに置き換えるのと同じくらい簡単です。

// <Name>Avoid methods of a type to be in cycles</Name>
warnif count > 0

from t in Application.Types
                 .Where(t => t.ContainsMethodDependencyCycle != null && 
                             t.ContainsMethodDependencyCycle.Value)

// Optimization: restreint methods set
// A method involved in a cycle necessarily have a null Level.
let methodsSuspect = t.Methods.Where(m => m.Level == null)

// hashset is used to avoid iterating again on methods already caught in a cycle.
let hashset = new HashSet<IMethod>()


from suspect in methodsSuspect
   // By commenting this line, the query matches all methods involved in a cycle.
   where !hashset.Contains(suspect)

   // Define 2 code metrics
   // - Methods depth of is using indirectly the suspect method.
   // - Methods depth of is used by the suspect method indirectly.
   // Note: for direct usage the depth is equal to 1.
   let methodsUserDepth = methodsSuspect.DepthOfIsUsing(suspect)
   let methodsUsedDepth = methodsSuspect.DepthOfIsUsedBy(suspect)

   // Select methods that are both using and used by methodSuspect
   let usersAndUsed = from n in methodsSuspect where 
                         methodsUserDepth[n] > 0 && 
                         methodsUsedDepth[n] > 0 
                      select n

   where usersAndUsed.Count() > 0

   // Here we've found method(s) both using and used by the suspect method.
   // A cycle involving the suspect method is found!
   // 8Feb2021: invoke extension method Append() explicitly to avoid ambiguous compiler error
   //           because of the new .NET BCL extension methods Append() method
   let cycle = NDepend.Helpers.ExtensionMethodsEnumerable.Append(usersAndUsed,suspect)

   // Fill hashset with methods in the cycle.
   // .ToArray() is needed to force the iterating process.
   let unused1 = (from n in cycle let unused2 = hashset.Add(n) select n).ToArray()

select new { suspect, cycle }

...そして、このルールの結果は次のようになります (メソッド サイクルを依存関係グラフまたはマトリックスにエクスポートする可能性はまだあります)。メソッドと型の数は名前空間とアセンブリの数よりもはるかに多いため、このクエリを Roslyn のような大規模なコード ベースで実行すると (名前空間サイクルの 16 ミリ秒ではなく) 約 10 秒かかることに注意してください。したがって、調整が必要になる場合があります。 CQLinq クエリ実行タイムアウト (デフォルトでは 2 秒)。

タイプ依存サイクル

完全にするために、私が気付いたのは、ほとんどの場合、いくつかの双方向の参照によってサイクルが引き起こされていることです (つまり、A は B を使用し、B は A を使用しています)。したがって、双方向参照を削除することは、サイクルを壊すために最初に行うことです。これが、デフォルトの CQLinq ルールの名前空間相互依存を回避する を提供した理由です。これは、型またはメソッドのサイクルに引き続き適応できます。

于 2013-02-28T08:47:56.673 に答える