ReSharper:呼び出し階層

ReSharper 5.0では、新しい呼び出し階層機能が導入されています。 本質的には、大量の使用状況を検索したり、宣言に移動したりするのに便利なUIです。

当初、この記事ではR#とVS 2010でこの機能の比較分析を行いたいと考えていましたが、執筆中にVS 2010の呼び出し階層は批判(イベント、インターフェイス、クロージャなどでは機能しません)および例には耐えられないことが判明しました記事からはまったく有用で合理的なものを示していません。 したがって、R#の呼び出し階層でできる興味深いことだけを説明します。


イベント


Fooメソッド( R#-> Inspect-> Outgoing )からの発信呼び出しを探してみましょう。

using System;

public class C2
{
public event EventHandler E = (sender, args) => Subscription_In_Initializer();

static void Subscription_In_Initializer()
{
}

void Foo()
{
E( this , EventArgs .Empty);
}
}

class C3
{
void Bar()
{
new C2().E += Subscription_In_Method;
}

void Subscription_In_Method( object sender, EventArgs e)
{
}
}


* This source code was highlighted with Source Code Highlighter .




結果は明白で理解しやすいものです。 R#は、すべてのevent Eサブスクリプションを安全に検索し、可能な呼び出しオプションとして表示します。 超自然的なものはありませんが、非常に快適です。

ジェネリック


簡単なコード例を考えてみましょう。

public abstract class Base<T>
{
public void Do(T value )
{
DoImplementation( value );
}

protected abstract void DoImplementation(T value );
}

public class Concrete1 : Base< int >
{
protected override void DoImplementation( int value )
{
}
}

public class Concrete2 : Base< string >
{
protected override void DoImplementation( string value )
{
}
}


* This source code was highlighted with Source Code Highlighter .


次に、 Base.Fooからの発信呼び出しをBase.Fooます。



すべてが明らかです。

次に、このようなMainクラスを追加し、 Fooからの発信呼び出しを探します。

class Main
{
void Foo()
{
Concrete2 c = null ; // null,
c.Do( "string" );
}
}


* This source code was highlighted with Source Code Highlighter .




Concrete1.DoImplementation呼び出してください! これは、R#が型パラメーターを見て、 Base.Do T->string呼び出されると結論付けたBase.Do (結果の2行目を参照してください: Base<string>.Do - stringは、特定の置換を持つメソッドを呼び出していることを示します)、それに応じてConcrete1フィルタリングしました、なぜなら 置換T->int使用して継承を使用します。

少し複雑ですが、Visitorパターンを使用したさらに重要な例です。 ConcreteVisitor1.VisitNode1メソッドへの着信呼び出しを見つけます( R#-> Inspect-> Incoming Calls )。 この例では、メソッド呼び出しに対して反対のように、すでに反対方向に進んでいることに注意してください。

public interface IVisitor<T>
{
void VisitNode1(T data);
}

class Node1
{
public void Accept<T>(IVisitor<T> v, T data)
{
v.VisitNode1(data);
}
}

public class ConcreteVisitor1 : IVisitor< int >
{
public void VisitNode1( int data)
{
}
}

public class ConcreteVisitor2 : IVisitor< string >
{
public void VisitNode1( string data)
{
}
}

public class C1
{
void Foo()
{
var v = new ConcreteVisitor1();
new Node1().Accept(v, 1);
}

void Foo2()
{
var v = new ConcreteVisitor2();
new Node1().Accept(v, "string" );
}
}


* This source code was highlighted with Source Code Highlighter .


結果は次のようになります。



ジェネリックビジターを通過しても、R#は型パラメーターの置換に関する情報を失わず、 Foo2メソッドからの余分な呼び出しを除外できました。 分岐階層と多数のジェネリック型を作成した場合、そのようなロジックは検索領域を劇的に削減できます。

コンストラクター


フィールドコンストラクターとイニシャライザーを使用した、やや人工的な例です。 Derivedクラスのコンストラクターからの発信呼び出しを探しています。

class Base
{
public Base()
{
Base_Bar();
}

void Base_Bar()
{
}
}

class Derived : Base
{
int _i = Foo();

public Derived()
{
Bar();
}

void Bar()
{
}

static int Foo()
{
return 0;
}
}


* This source code was highlighted with Source Code Highlighter .




繰り返しますが、異常なことは何もありません。 R#は、基本コンストラクターへの暗黙的な呼び出しを忘れずに、呼び出しの自然な順序を単に表示します。 これにより、経験の浅い開発者がコードを理解する時間を大幅に節約でき、疲れた経験のある開発者に役立ちます。

値の追跡。 結論の代わりに。


おやつに。 メニューR#-> Inpectを見ると、2つの非常に興味深い項目「Value Origin」と「Value Destination」があります。 これらの2つの関数は、Data Flow Analysysを実装しています。 どこから来たのか、変数やパラメータの値がどこに行くのかを追跡できます。 当然、DFAはコレクションとデリゲートで動作し(コレクションからアイテムが取得され、コレクションへのアクセスの検索に進むという事実を追跡します)、 NullReferenceException原因を見つけるのに絶対に不可欠です。 残念ながら、これには多くのスクリーンショットと例が必要になるため、次の記事でこれについて詳しく説明しますが、今のところは、DFAを自分で試して、好きなものとそうでないものについて話すことができます。

Source: https://habr.com/ru/post/J83769/


All Articles