Используя Expression API, есть ли способ «объединить» Func <T>с другим? - PullRequest
0 голосов
/ 05 августа 2011

Скажем, у меня есть такой метод:

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

Можно ли как-нибудь использовать API Expression и эффективно внедрить некоторый код для запуска до запуска кода из переданного в Func? Это означает, что в моем методе я на самом деле не буду выполнять Func, а скорее назначу ему какое-то свойство объекта, который вызовет этот Func. Я просто хочу, чтобы какой-то код выполнялся до запуска переданного кода. Кроме того, возможно ли, чтобы мой код "вернулся" из Func, чтобы переданный код никогда не выполнялся?

РЕДАКТИРОВАТЬ: ОК, я боялся, что этого было недостаточно. Вот что я пытаюсь сделать. Используя Moles, вы можете заглушить любой звонок, сделанный в вашем коде. Допустим, у меня есть некоторый непроверяемый код, который вызывает статический метод, например:

FileSystem.ReadAllText(string fileName);

Кроты создадут объект-заглушку / моль (независимо от терминологии), который я затем смогу использовать для заглушки метода, поэтому, когда мой код вызывает ReadAllText, он вызовет мою заглушку:

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

Очень классная штука, однако я пытаюсь расширить эту идею, чтобы я мог использовать ее подобно типичной среде Mocking (например, Moq), чтобы я мог заглушить этот метод только тогда, когда переданные параметры являются определенное значение или, возможно, чтобы убедиться, что метод вызывается только один раз и т. д. Вот что у меня есть:

ЕЩЕ ОЧЕНЬ ОЧЕНЬ ЖЕСТКО !!

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

Идея в том, что если у меня есть такой класс:

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

    public void LoadFile(string fileName)
    {
        var content = FileSystem.ReadAllText(fileName);
        if (!content.StartsWith("test"))
        {
            throw new ArgumentException("invalid file");
        } 
        this.Content = content;
    }
}

Теперь я могу сделать это:

    [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);
    }

Пока все хорошо, но я еще ничего не достиг. Что я хочу сделать, это в моем методе расширения, когда я делаю это:

            property.SetValue(item, stub, null);

, который установит делегату Moles все, что передается, я хочу заранее «вставить» некоторый код, чтобы я мог видеть, какие значения передаются, я могу добавить некоторые данные в некоторый словарь, чтобы сохранить отслеживание количества вызовов методов и т. д.

Надеюсь, теперь это имеет больше смысла ...

1 Ответ

3 голосов
/ 05 августа 2011

Почему бы вам просто не обернуть его, прежде чем присвоить его свойству?

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