Reputation: 63816
The exact reasons I might want to do this are a bit hard to go into without posting loads of code, but here's an example of one scenario:
try
{
x.Method(123,"test");
Assert.Fail("No exception thrown");
}
catch (ApplicationException e)
{
StringAssert.StartsWith(e.Message, "Oh no!");
}
I'd like to refactor this to something like... (pseudo-code)
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);
}
}
I know C# is pretty flexible with lambdas and so on, but is this pushing it too far or is it reasonable straightforward?
Upvotes: 4
Views: 67
Reputation: 149050
Yes, and it's pretty straight forward. You can pass a Action
delegate (or really any delegate type) into a method and call it:
void TestExceptionMessage(Action action, string message)
{
try
{
action();
Assert.Fail("No exception thrown");
}
catch (ApplicationException e)
{
StringAssert.StartsWith(e.Message, message);
}
}
That delegate can be a reference to a method or it could be a lambda expression like this:
var x = new MyClass();
TestExceptionMessage(() => x.Method(123, "test"),"Oh no!");
However, if you really want to know the expression being used (e.g. if you want to be able to tell what method is being called) you need to use an Expression<Action>
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);
}
}
And thanks to a little compiler magic, you can call with the same lambda syntax:
var x = new MyClass();
TestExceptionMessage(() => x.Method(123, "test"),"Oh no!");
Upvotes: 6