2014-02-04 1 views
2

В C#, когда я вызываю метод, я хочу иметь возможность определить, сможет ли он (или потенциально) вызвать что-то с определенным атрибутом.Есть ли способ обнаружить, что будет вызвано методом?

Например, когда вызывается вызов TheProgram.Run() get, я хочу знать, что он вызовет MyClass.DoTheWork, у которого есть атрибут [IsRegistered], который вызывает частный метод FormatTheResult(), который также имеет attribute [IsRegistered].

Я размышлял об этом некоторое время и не могу представить, как это можно достичь. Я думаю, что-то вроде инверсии трассировки стека или регистрации компонентов с атрибутом или аспектом или, возможно, опираясь на MEF.

Возможно ли это?

Это обнаружение может произойти во время компиляции или времени выполнения, но в идеале до того, как будет выполнен метод с атрибутом.

+0

Я точно не знаю, как, но это, безусловно, возможно. Например, в анализе кода используется так называемая «интроспекция».Если ваша цель - провести проверку какого-либо теста (а модульные тесты недействительны), посмотрите на создание настраиваемого правила анализа кода. Кроме того, Mocking frameworks позволяет найти и заменить вызовы, хотя я не уверен, как они точно работают или что их метод делает так. –

ответ

2

Mocking frameworks может это сделать. Это полезно для поведенческих тестов.

Например, учитывая эту установку:

public class Calculator { 
    private IHelpers _helperMethods; 

    public Calculator(IHelpers helper) { 
     _helperMethods = helper; 
    } 

    public int Add(int a, int b) { 
     if (_helperMethods.AboveZero(a) && _helperMethods.AboveZero(b)) { 
      return a + b; 
     } 
     throw new Exception("Argument not above zero"); 
    } 
} 

public interface IHelpers { 
    bool AboveZero(int i); 
} 

Используя Moq, вы можете проверить (с помощью модульного тестирования поведенческой), что IHelpers.AboveZero вызывается при вызове Add метод, как так:

[TestMethod] 
public void When_Add_Called_Verify_AboveZero_Called_Too() { 
    // Arrange 
    var helperMock = new Mock<IHelpers>();  
    helperMock.Setup(x => x.AboveZero(It.IsAny<int>())).Returns(true); 

    var calc = new Calculator(helperMock.Object); 

    // Act 
    var result = calc.Add(1, 2); 

    // Assert 
    helperMock.Verify(x => x.AboveZero(It.IsAny<int>())); // verify that AboveZero was called. 
} 

Атрибуты - это другая история, хотя ..

Это вы, что искали?

(Пожалуйста, простите ошибки компилятора .. это типизированное вручную: /)

+0

Хорошее мышление, спасибо! Тем не менее, я на самом деле надеюсь сделать обнаружение до того, как метод будет выполнен (не обязательно использовать атрибут, хотя), так что это не совсем то, что мне нужно. Мне нужно каким-то образом определить, что будет выполнено до его запуска. Даже обнаружение того, что МОЖЕТ быть выполнено, будет достаточно хорошим. –

1

То, что вы, вероятно, ищете это Рослины.

http://msdn.microsoft.com/en-au/vstudio/roslyn.aspx

Что вы можете сделать с этим анализирует синтаксическое дерево непосредственно, так и для вашего метода вопроса, который вы могли бы получить доступ из синтаксического дерева всех вызовов методов, которые происходят. Затем вы можете следить за этим и проверять, что вызываемый метод имеет этот атрибут.

Это довольно сложный материал, поэтому я не буду пытаться использовать образец кода для вашего конкретного сценария, но я использовал его раньше, чтобы проанализировать несколько запросов и ввести код.

Это довольно здорово, вот образец из документов.

namespace GettingStartedCS 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            SyntaxTree tree = SyntaxTree.ParseCompilationUnit(
@"using System; 
using System.Collections; 
using System.Linq; 
using System.Text; 

namespace HelloWorld 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            Console.WriteLine(""Hello, World!""); 
        } 
    } 
}"); 

            var root = (CompilationUnitSyntax)tree.GetRoot(); 

            var firstMember = root.Members[0]; 

            var helloWorldDeclaration = (NamespaceDeclarationSyntax)firstMember; 

            var programDeclaration = (TypeDeclarationSyntax)helloWorldDeclaration.Members[0]; 
  
            var mainDeclaration = (MethodDeclarationSyntax)programDeclaration.Members[0]; 
  
            var argsParameter = mainDeclaration.ParameterList.Parameters[0]; 

        } 
    } 
} 
+0

Ничего себе, это действительно здорово. Я этого раньше не видел. Я посмотрю на это сейчас. Спасибо @shenku. –

+0

не проблема, это твоя голова немного, хотя и такая удача. Одна вещь, чтобы следить, хотя это производительность, работа, которую я сделал, периодически запускалась против кода, и я не знаю, как это будет происходить во время выполнения. Поэтому следите за этим. – shenku

+0

Да, это была моя первая мысль. Мой код должен работать ... много. Не может быть жизнеспособным, но супер интересным. Еще раз спасибо. Будет обновление, но мне понадобится немного времени, чтобы посмотреть, подходит ли она. –

0

Resharper делает то, что вы хотите. Выполните команду меню Resharper -> Inspect -> Исходящие вызовы, а затем разверните узлы дерева до бесконечности до тех пор, пока не достигнете желаемого метода. Если вы используете отражение или что-то подобное, вы, кажется, не повезло. Ниже приведен пример того, как это работает.

enter image description here

+0

Спасибо за ввод. Это доказывает, что это возможно. Приветственный помощник. –

0

Это способ, которым я нашел, чтобы сделать это:

public static IList<MethodBase> GetCalledMethods(MethodBase methodBase) 
{ 
    IList<MethodBase> calledMethods = new List<MethodBase>(); 
    var body = methodBase.GetMethodBody(); 
    Module module = Assembly.GetExecutingAssembly().ManifestModule; 
    byte[] bytes = body.GetILAsByteArray(); 
    using (var stream = new MemoryStream(bytes)) 
    { 
     long streamLength = stream.Length; 
     using (var reader = new BinaryReader(stream)) 
     { 
      while (reader.BaseStream.Position < streamLength) 
      { 
       byte instruction = reader.ReadByte(); 
       if (instruction == OpCodes.Call.Value 
        || instruction == OpCodes.Callvirt.Value 
        || instruction == OpCodes.Newobj.Value) 
       { 
        int token = reader.ReadInt32(); 
        var method = module.ResolveMethod(token); 
        calledMethods.Add(method); 
       } 
      } 
     } 
    } 

    return calledMethods; 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^