开发者

Using the Expression API, is there a way to "combine" a Func<T> with another?

开发者 https://www.devze.com 2023-03-26 09:35 出处:网络
Say I have some method like so: public void Method<T>(Func<T> func) { ... } Is there any way that I can use the Expression API and effectively inject some code to run before the code in

Say I have some method like so:

public void Method<T>(Func<T> func)
{
...
}

Is there any way that I can use the Expression API and effectively inject some code to run before the code in the passed in Func runs? Meaning, in my method, I won't actually execute the Func, rather I'll be assigning it some property of an object that will invoke that Func. I just want to have some code run before the passed in code runs. Also, would it be possible for my code to "return" from the Func, so that the passed in code never runs?

EDIT: OK, I was afraid that wasn't enough. Here's what I'm trying to do. Using Moles, you can stub out any call made within your code. So say I have some untestable code that calls a static method like so:

FileSystem.ReadAllText(string fileName);

moles will create a stub/mole object (whatever the terminology is) which I can then use to stub out the method for, so when my code calls ReadAllText, it'll call my stub:

MFileSytem.ReadAllTextString = s => return "content";

Very cool stuff, however I'm trying to expand on this idea, so that I can use it similiar to a typical Mocking framework (like Moq) so that I can stub out that method only when the passed in parameters is a certain value, or possibly to verify that a method is only called once etc. Here's what I have so far:

STILL VERY VERY ROUGH!!

    public static class Extensions
    {
        public static void TestWithMoles<T, U, V>(this T item, Expression<Func<U, V>> expression, MolesDelegates.Func<U, V> stub)
        {
            var methodCallExpression = expression.Body as MethodCallExpression;
            if (methodCallExpression != null)
            {
                var method = methodCallExpression.Method;
                var returnType = method.ReturnType.Name;
                var assemblyName = method.DeclaringType.Assembly;
                var assmebly = GetMolesAssmeblyName(assemblyName.GetName().Name);
                var type = assmebly.GetTypes().FirstOrDefault(t => t.Name == "M" + method.DeclaringType.Name);
                var property = type.GetProperties(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(p => p.Name == method.Name + returnType);
                property.SetValue(item, stub, null);
            }
        }

        private static Assembly GetMolesAssmeblyName(string assemblyName)
        {
            var entryAssembly = Assembly.GetExecutingAssembly();
            foreach (var assembly in entryAssembly.GetReferencedAssemblies())
            {
                if (assembly.Name == assemblyName)
                {
                    continue;
                }

                if (assembly.Name.Contains(assemblyName) && assembly.Name.Contains(".Moles"))
                {
                    return Assembly.Load(assembly.FullName);
                }
            }

            return null;
        }
    }

The idea now is, if I have a class like this:

public class TestReader
{
    public string Content { get; private set; }

    public void LoadFile(st开发者_开发技巧ring fileName)
    {
        var content = FileSystem.ReadAllText(fileName);
        if (!content.StartsWith("test"))
        {
            throw new ArgumentException("invalid file");
        } 
        this.Content = content;
    }
}

I can now do this:

    [TestMethod]
    [HostType("Moles")]
    public void CheckValidFilewithMoles()
    {
        //arrange
        var fileName = "test.txt";
        var content = "test";

        //act
        var test = new TestReader();
        test.TestWithMoles<TestReader, string, string>(s => FileSystem.ReadAllText(s), s =>
                                                                                            {
                                                                                                Assert.IsTrue(s == fileName);
                                                                                                return content;
                                                                                            });
        test.LoadFile(fileName);
        //assert
        Assert.AreEqual(content, test.Content);
    }

So far so good, but I haven't accomplished anything yet. What I WANT to do is, in my extension method, when I do this:

            property.SetValue(item, stub, null);

which will set the Moles delegate to whatever is passed in, I want to "inject" some code beforehand, so that I can see what the values are that are being passed in, I can add some data to some dictionary to keep track of the count of method calls, etc.

Hope that makes more sense now...


Why don't you just wrap it before assigning it to the property ?

Func<T> myProp = () => 
{ 
  //run your code here
  return func();
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号