Approaches to handling simple expressions in C#
Tags: csharp, roslyn, dotnet
Published: 2021-08-30 13:47:11


---
title: Approaches to handling simple expressions in C# 
published: true
description: Every now and then we get asked if there&#8217;s an easy way to parse user input into filter conditions. Say, for example, we have a viewmodel of type DataThing: From &hellip; Continue reading &#8220;Approaches to handling simple expressions in C#&#8221;
tags: csharp, roslyn, dotnet
canonical_url: https://blog.wiseowls.co.nz/index.php/2021/01/27/approaches-to-handling-simple-expressions-in-c/
cover_image: https://blog.wiseowls.co.nz/wp-content/uploads/2020/03/technology-computer-desktop-programming-113850_cr-scaled.jpg
---

Every now and then we get asked if there’s an easy way to parse user input into filter conditions. Say, for example, we have a viewmodel of type `DataThing`:

```csharp
public class DataThing
 {
     public string Name;
     public float Value;
     public int Count;
 }
```


From here we’d like to check if a given property of this class satisfies a certain condition. For example, we’ll look at “Value is greater than 15”. But of course, we’d like to be flexible.

The issue
---------

The main issue here is we don’t know the type of property beforehand, so we can’t use generics even if we try to be smart:

```csharp
public class DataThing
 {
     public string Name;
     public float Value;
     public int Count;
 }
 public static void Main()
 {
     var data = new DataThing() {Value=10, Name="test", Count = 1};
     var values = new List {
         new ValueGetter(x => x.Value),
         new ValueGetter(x => x.Name)
     };
     (values[0].Run(data) > 15).Dump();
 }
 public abstract class ValueGetter
 {
     public abstract T Run<T>(DataThing d);
 }
 public class ValueGetter<T> : ValueGetter
 {
     public Func<DataThing, T> TestFunc;
     public ValueGetter(Func<DataThing, T> blah)
     {
         TestFunc = blah;
     }
     public override T Run(DataThing d) => TestFunc.Invoke(d); // CS0029 Cannot implicitly convert type…
 }
```


Even if we figured it out it’s obviously way too dependant on `DataThing` layout to be used everywhere.

LINQ Expression trees
---------------------

One way to solve this issue is with the help of LINQ expression trees. This way we wrap everything into one delegate with predictable signature and figure out types at runtime:

```csharp
 bool BuildComparer(DataThing data, string field, string op, T value) {    
     var p1 = Expression.Parameter(typeof(DataThing));
     var p2 = Expression.Parameter(typeof(T));
     if (op == ">")
     {
         var expr = Expression.Lambda>(
             Expression.MakeBinary(ExpressionType.GreaterThan
                                 , Expression.PropertyOrField(p1, field)
                                 , Expression.Convert(p2, typeof(T))), p1, p2);
         var f = expr.Compile();
         return f(data, value);
      <span style="background-color: inherit; font-family: monospace; font-size: inherit;">} </span>
      <span style="background-color: inherit; font-family: monospace; font-size: inherit;">return false;</span>
 }
```


Code DOM CSharpScript
---------------------

Another way to approach the same problem is to generate C# code that we can compile and run. We’d need `Microsoft.CodeAnalysis.CSharp.Scripting` package for this to work:

```csharp
bool BuildScript(DataThing data, string field, string op, T value)
 {
     var code = $"return {field} {op} {value};";
     var script = CSharpScript.Create(code, globalsType: typeof(DataThing), options: ScriptOptions.Default);
     var scriptRunner = script.CreateDelegate();
     return scriptRunner(data).Result;
 }
```


.NET 5 Code Generator
---------------------

This is a new [.NET 5 feature](https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/), that allows us to plug into compilation process and generate classes as we see fit. For example, we’d generate extension methods that would all return correct values from `DataThing`:

```csharp
[Generator] // see https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md for even more cool stuff
 class AccessorGenerator: ISourceGenerator {
     public void Execute(GeneratorExecutionContext context) {
       var syntaxReceiver = (CustomSyntaxReceiver) context.SyntaxReceiver;
       ClassDeclarationSyntax userClass = syntaxReceiver.ClassToAugment;
       SourceText sourceText = SourceText.From($ @ "
         public static class DataThingExtensions {
           { 
           // This is where we'd reflect over type members and generate code dynamically. Following code is oversimplification
             public static string GetValue<string>(this DataThing d) => d.Name;
             public static string GetValue<float>(this DataThing d) => d.Value;
             public static string GetValue<int>(this DataThing d) => d.Count;
           }
         }
         ", Encoding.UTF8);
         context.AddSource("DataThingExtensions.cs", sourceText);
       }
       public void Initialize(GeneratorInitializationContext context) {
         context.RegisterForSyntaxNotifications(() => new CustomSyntaxReceiver());
       }
       class CustomSyntaxReceiver: ISyntaxReceiver {
         public ClassDeclarationSyntax ClassToAugment {
           get;
           private set;
         }
         public void OnVisitSyntaxNode(SyntaxNode syntaxNode) {
           // Business logic to decide what we're interested in goes here
           if (syntaxNode is ClassDeclarationSyntax cds &&
             cds.Identifier.ValueText == "DataThing") {
             ClassToAugment = cds;
           }
         }
       }
     }
```


Running this should be as easy as calling extension methods on the class instance: `data.GreaterThan(15f).Dump();`