Foo
method ( 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 .
event E
subscriptions and displays them as possible call options. Nothing extraordinary, but very convenient.
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
:
Main
class and try to look for outgoing calls from Foo
:
class Main
{
void Foo()
{
Concrete2 c = null ; // null,
c.Do( "string" );
}
}
* This source code was highlighted with Source Code Highlighter .
Concrete1.DoImplementation
is no longer a challenge! That's because R # looked at the type parameters and concluded that Base.Do
called with T->string
(see the second line of results: Base<string>.Do
- string
indicates that we are calling a method with a specific substitution ), and accordingly filtered Concrete1
, because it uses inheritance with the substitution T->int
.
ConcreteVisitor1.VisitNode1
( R # -> Inspect -> Incoming Calls ). Please note that in this example we are already going in the opposite direction, as if against method 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 .
Foo2
method. In cases where you have a spreading hierarchy and a large number of generic types, this logic allows you to drastically reduce the search area.
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 .
NullReferenceException
. Unfortunately, this will require me to have a whole bunch of screenshots and examples, so I will write more about it in the next article, but for now you can try DFA yourself and tell you what you liked and what didn't.Source: https://habr.com/ru/post/83769/