【问题标题】:Pass method name from property to lambda without arguments将方法名称从属性传递给 lambda,不带参数
【发布时间】:2014-03-07 09:45:59
【问题描述】:

我正在努力实现以下目标:

this.helper.Verify(i => i.SomeModule.SomeMethod);

地点:

  • i 是已知类型。
  • SomeModule 是这种类型的公共属性。
  • SomeMethod 是方法的名称(我假设是作为 delegate 传递的)。

我希望能够在Verify 方法中避免指定泛型。

这就是我能走多远:

public void Verify<TProp, TResult>(
            Expression<Func<KnownType, TProp>> moduleExpression,
            Expression<Func<TProp, TResult>> methodExpression)
{
    var moduleIdentifier = (moduleExpression.Body as MemberExpression).Member.Name;
    var methodIdentifier = (methodExpression.Body as MethodCallExpression).Method.Name;

    this.Verify(moduleIdentifier, methodIdentifier, state);
}

但这迫使我为 methodExpression 编写参数值,例如:

this.helper.Verify(
    i => i.SomeModule,
    m => m.SomeMethod(arg1, arg2));

有没有办法实现我正在寻找的东西?

【问题讨论】:

    标签: c# methods lambda delegates expression


    【解决方案1】:

    最后我得到了一个需要委托的解决方案。

    我需要分析一个可以有多个参数的表达式,例如:

    i => i.Car.Engine.Intake.Open()
    

    我会将最后两个参数提取为“模块”和“方法”:

    public class ExpressionTools
    {
        public string GetLastInstanceName<TProp, TDelegate>(Expression<Func<TProp, TDelegate>> expression)
        {
            var unaryExpression = (UnaryExpression)expression.Body;
            var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
            var methodInfoExpression = (MemberExpression)methodCallExpression.Arguments.Reverse().Skip(1).First();
            var instanceName = methodInfoExpression.Member.Name;
    
            return instanceName;
        }
    
            public string GetMethodName<TProp, TDelegate>(Expression<Func<TProp, TDelegate>> expression)
        {
            var unaryExpression = (UnaryExpression)expression.Body;
            var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
            var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last();
            var methodInfo = (MemberInfo)methodInfoExpression.Value;
    
            return methodInfo.Name;
        }
    }
    

    代表解决方案

    可以重用具有泛型的类,因为委托不需要分析表达式:

    public class Verifier
    {
        public void Verify<TDelegate>(Expression<Func<KnownType, TDelegate>> methodExpression)
        {
            var tools = new ExpressionTools();
            var moduleName = tools.GetLastInstanceName(methodExpression);
            var methodName = tools.GetMethodName(methodExpression);
    
            // My logic.
        }
    }
    

    现在我需要创建代表,为此我创建了一个文件并在没有类的情况下声明它们:

    namespace Whatever
    {
        public delegate void VoidDelegate();
        public delegate void VoidBoolDelegate(bool value);
        // etc...
    }
    

    最后,我需要维护一个类,该类具有用不同委托重载的相同方法。

    public class FakeExtensions
    {
        public static void Verify(
            this Fake fake, 
            Expression<Func<KnownType, VoidDelegate>> methodExpression)
        {
            var tools = new ExpressionTools();
            var facilitator = new Verifier(fake, tools);
            facilitator.Verify(methodExpression);
        }
    
        public static void Verify(
            this Fake fake, 
            Expression<Func<KnownType, VoidBoolDelegate>> methodExpression)
        {
            var tools = new ExpressionTools();
            var facilitator = new Verifier(fake, tools);
            facilitator.Verify(methodExpression);
        }
    }
    

    没有代表的替代解决方案

    最简单的解决方案是为每个参数传递一个默认值,但这表示这些值也经过验证,但事实并非如此。我真的不喜欢有这样的想法:

    this.helper.Verify(i => i.SomeModule.SomeMethod(0, false, 0.0, null);
    

    由于 Moq 是我们测试框架的一部分,因此可以使用 It.IsAny 作为方法调用的每个参数的值,这表示值无关紧要:

    this.helper.Verify(i => i.SomeModule.SomeMethod(It.IsAny<int>, It.IsAny<bool>, It.IsAny<double>, It.IsAny<SomeType>);
    

    我为什么选择委托解决方案

    我必须验证对具有多达 100 个参数的方法的低级第 3 方 API 上的一些调用。我认为维护委托列表和方法重载的负担在可维护性、可读性和易用性方面得到了回报。

    【讨论】:

      猜你喜欢
      • 2014-08-27
      • 1970-01-01
      • 2021-11-30
      • 1970-01-01
      • 1970-01-01
      • 2013-10-08
      • 2012-11-06
      • 2018-09-11
      • 2014-03-22
      相关资源
      最近更新 更多