dynamicexpresso / DynamicExpresso

C# expressions interpreter
http://dynamic-expresso.azurewebsites.net/
MIT License
1.91k stars 364 forks source link

NullReferenceException using variables in custom functions #284

Open ArdenCarl opened 1 year ago

ArdenCarl commented 1 year ago

We have a use case where we are defining our own custom IF function and then making use of it using an expression like so:

IF(CUST1 != null, CUST1.Name, "Cust1 is null")

The problem we are facing is when the CUST1 variable turns out to be a null instance then we get a null reference exception when we call Interpreter.Eval for the expression.

Here's the complete code:

image

I've also attached the .cs file for it as a .txt file since I don't seem to be able to attach .cs files directly. See Tester.txt.

Tester.txt

ArdenCarl commented 1 year ago

I forgot to mention, I appreciate the interpreter doesn’t know not to deference CUST1 and so hence the exception. My question is, is it possible somehow to implement such an IF function?

If not, then how would we write an if block? I know we could use a ternary operator but I’m trying to avoid my users having to use that.

davideicardi commented 1 year ago

Adding the relavant code here:

using System;
using DynamicExpresso;

namespace DETest
{
    public class Tester
    {
        public class Customer
        {
            public string Name { get; set; }
        }

        public void Test()
        {
            var interpreter = new Interpreter();

            Func<bool, object, object, object> ifFunction = (b, x, y) => If(b, x, y);
            interpreter.SetFunction("IF", ifFunction);

            var cust1 = new Customer { Name = "Customer1" };

            interpreter.SetVariable("CUST1", cust1, typeof(Customer));

            // note. how I pass null for the CUST2 variable value here
            interpreter.SetVariable("CUST2", null, typeof(Customer));

            var exp1 = interpreter.Parse("IF(CUST1 != null, CUST1.Name, \"Cust1 is null\")").Expression;
            interpreter.SetExpression("EXP1", exp1);

            var exp2 = interpreter.Parse("IF(CUST2 != null, CUST2.Name, \"Cust2 is null\")").Expression;
            interpreter.SetExpression("EXP2", exp2);

            // this works
            System.Diagnostics.Debug.WriteLine(interpreter.Eval("EXP1"));

            // this throws an exception
            System.Diagnostics.Debug.WriteLine(interpreter.Eval("EXP2"));
        }

        private object If(bool b, object x, object y)
        {
            return b ? x : y;
        }
    }
}
davideicardi commented 1 year ago

The problem is that the parameter is evaluated always, it is not dependent on the condition.

Why not using the standard ?: conditional operator? Something like:

CUST2 != null ? CUST2.Name : "Cust2 is null"
ArdenCarl commented 1 year ago

Many thanks for your prompt response @davideicardi.

As mentioned in my comment, we could use the ternary operator but the reason being our end users are much more familiar with Excel style functions. I guess it's asking a bit much for the interpreter to understand the desired IF function, which I appreciate.

Is it at all possible to write an if/else statement? My users would understand this more. I have tried but do not seem to get it to work.

davideicardi commented 1 year ago

Writing an if/else is not supported. Maybe you can try to do some kind of custom "pre processing" of the expression and convert the IF() function to a ternary?

hzy-6 commented 7 months ago

You may need it https://github.com/ncalc/ncalc