无论如何都要检测从方法调用的内容?

时间:2014-02-04 04:08:28

标签: c# mef aop

在C#中,当我调用一个方法时,我希望能够检测它是否(或可能)调用具有某个属性的东西。

例如,当调用TheProgram.Run()时,我想知道它会调用一个MyClass.DoTheWork,它有一个属性[IsRegistered],它调用私有方法FormatTheResult(),它也有属性[IsRegistered]。

我已经考虑了一段时间,无法想象它是如何实现的。我在想,就像堆栈跟踪的反转,或者注册具有属性或方面的组件,或者可能依赖于MEF。

这可能吗?

这种检测可能发生在编译时或运行时,但理想情况是在执行带有属性的方法之前。

4 个答案:

答案 0 :(得分:2)

模拟框架可以做到这一点。它对行为测试很有用。

例如,给定此设置:

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.
}

虽然这些属性是一个不同的故事..

这是你追求的吗?

(请原谅任何编译错误..这是手工输入的:/)

答案 1 :(得分:1)

你可能正在寻找的是罗斯林。

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

您可以使用此方法直接分析语法树,因此对于您所使用的方法,您可以从语法树中访问所有发生的方法调用。然后你可以按照这个并检查被调用的方法是否具有该属性。

是非常复杂的东西,所以我不会为你的特定场景尝试代码示例,但我之前使用它来分析多个solitions并注入代码。

这是非常棒的,这是来自文档的样本。

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];

        }
    }
}

答案 2 :(得分:0)

Resharper可以满足您的需求。执行菜单命令Resharper - &gt;检查 - &gt;拨出电话,然后无限扩展树节点,直到达到所需的方法。如果你正在使用反射或类似的东西,我猜你运气不好。下面的图片是它如何运作的一个例子。

enter image description here

答案 3 :(得分: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;
}