I have a delegate
as follows:
public delegate TestResult TestCase(byte[] source);
...where the return TestResult
is as follows:
public class TestResult {
public bool Pass { get; }
public int Index { get; }
public TestResult(bool result, int index) {
Pass = result;
Index = index;
}
}
An example TestCase
delegate looks like:
public static TestResult In(byte[] tkn, ITestSet testSet) {
return testSet.Contains(tkn);
}
ITestSet
is not much more than an encapsulated HashSet<byte[]>
.
In one use case I have 2 test sets: (1) A - z, (2) 0 - 9. I want to test if an input byte[]
is in either test set.
I am using Expression<TestCase>
but having trouble figuring out how to implement the Or
test case. I have a TestCaseBuilder
with the following methods:
public class TestCaseBuilder {
private Expression<TestCase> tcExpr;
public TestCaseBuilder With(TestCaseBuilder tcBuilder) {
tcExpr = tcBuilder.tcExpr;
return this;
}
public TestCaseBuilder Or(TestCaseBuilder tcBuilder) {
tcExpr = tcExpr.Or(tcBuilder.tcExpr);
return this;
}
}
...and my extension method:
public static Expression<TestCase> Or (this Expression<TestCase> tcLeft, Expression<TestCase> tcRight) {
var lExpr = (LambdaExpression)tcLeft;
var rExpr = (LambdaExpression)tcRight;
var param = lExpr.Parameters;
return Expression.Lambda<TestCase>(/* what to do here ? */, param);
}
Expression.OrElse
is mechanically what I would think is appropriate but cannot use that since I am returning a TestResult
, not a bool
.
Here is how the TestCaseBuilder
is used:
testcaseBuilder.As("Foo")
.With(isLetterTestCase)
.Or(isDigitTestCase);
I have performed the Or
using just the TestCase
delegates:
public static TestCase Or(this TestCase tc1, TestCase tc2) {
return tkn => {
var res = tc1(tkn);
if (res.Pass) {
return res;
}
return tc2(tkn);
};
}
How can I combine the 2 Expression<TestCase>
in a custom Or
method without invoking the first test case?
Is this what you wanted
public static Expression<Func<byte[], TestResult, TestCase, TestResult>> helperExp = (inp, res, next) => res.Pass ? next(inp) : res;
public static Expression<TestCase> Or(Expression<TestCase> exp1, Expression<TestCase> exp2)
{
var param = exp1.Parameters;
Expression<TestCase> or = Expression.Lambda<TestCase>(
Expression.Invoke(helperExp,
param[0], Expression.Invoke(exp1, param), exp2),param);
return or;
}
With a block expression no invoke
public static Expression<TestCase> Or(Expression<TestCase> exp1, Expression<TestCase> exp2)
{
var param = exp1.Parameters;
ParameterExpression local = Expression.Parameter(typeof(TestResult), "local");
BlockExpression block = Expression.Block(
new[] { local },
Expression.Assign(local, exp1.Body),
Expression.Condition(Expression.Property(local, nameof(TestResult.Pass)), exp2.Body, local));
return Expression.Lambda<TestCase>(block, param);
}
Test
Expression<TestCase> exp1 = (tc) => new TestResult(true);
Expression<TestCase> exp2 = (tc) => new TestResult(false);
var first = Or(exp1, exp1);
var second = Or(first, exp2);
var func = second.Compile();
var result = func(new byte[] { });
There might be a better way to do a conditional monad without expressions using https://github.com/louthy/csharp-monad I thing .net core uses the monad principle for middleware.