我可以创建一个实用的方法,它将任意表达式作为参数并调用它吗?

时间:2016-05-04 15:16:51

标签: c# .net lambda

我可能想要做到这一点的确切原因在没有发布大量代码的情况下有点难以进行,但这是一个场景的示例:

try
{
    x.Method(123,"test");
    Assert.Fail("No exception thrown");
}
catch (ApplicationException e)
{
    StringAssert.StartsWith(e.Message, "Oh no!");
}

我想将其重构为......(伪代码)

TestExceptionMessage({x.Method(123,"test")},"Oh no!");

void TestExceptionMessage(func,string message)
{
    try
    {
        func();
        Assert.Fail("No exception thrown");
    }
    catch (ApplicationException e)
    {
        StringAssert.StartsWith(e.Message, message);
    }
}

我知道C#在lambda等方面非常灵活,但这是推动它走得太远还是合理的直截了当?

1 个答案:

答案 0 :(得分:6)

是的,而且非常直截了当。您可以将Action委托(或任何委托类型)传递给方法并调用它:

<xsl:variable name="deal-rtf">
  <xsl:choose>
     <xsl:when test="...">
       <xsl:copy-of select="/webpage/results/cars/*[partner_name = $company_name and vehicle_class_description = $vehicle_class_description_full and pay_now = 'Y']"/>
     </xsl:when>
     <xsl:otherwise>
        <xsl:copy-of select="webpage/results/cars/*[partner_name = $company_name and vehicle_class_description = $vehicle_class_description_full]"/>
     </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

<xsl:variable name="deal" select="exsl:node-set($deal-rtf)" xmlns:exsl="http://exslt.org/common"/>

<xsl:value-of select="$deal/total_price"/>

该委托可以是对方法的引用,也可以是这样的lambda表达式:

void TestExceptionMessage(Action action, string message)
{
    try
    {
        action();
        Assert.Fail("No exception thrown");
    }
    catch (ApplicationException e)
    {
        StringAssert.StartsWith(e.Message, message);
    }
} 

但是,如果你真的想知道正在使用的表达式(例如,如果你想知道正在调用哪种方法),你需要使用Expression<Action>

var x = new MyClass();
TestExceptionMessage(() => x.Method(123, "test"),"Oh no!");

感谢一点点编译魔术,你可以使用相同的lambda语法调用:

void TestExceptionMessage(Expression<Action> expression, string message)
{
    try
    {
        var methodName = (expression.Body as MethodCallExpression)?.Method.Name;
        if (methodName != null) 
        { 
            Debug.WriteLine($"Calling {methodName}");
        }
        expression.Compile().Invoke();
        Assert.Fail("No exception thrown");
    }
    catch (ApplicationException e)
    {
        StringAssert.StartsWith(e.Message, message);
    }
}