Comparing Delegates in C# - c#

I need to compare C# delegates for equality. I consider two delegates to be equal if they invoke the same method on the same instance of an object (or static) or if their method bodies have the exact same compiled IL. The code below includes the test cases I need the comparison to pass:
using System;
namespace ConsoleApplication
{
public delegate int Compare<Type>(Type left, Type right);
public delegate int Compare<Left, Right>(Left left, Right right);
public class Program
{
public static void Main(string[] args)
{
// Test 0 (false control)
Action _0_1 = () => { };
Action<int> _0_2 = (int i) => { Math.Sign(i); };
Console.WriteLine("0:\t" + (Equate(_0_1, _0_2) == false));
// Test 1s (same type delegates from static-method)
Compare<int> _1s_1 = Test;
Compare<int> _1s_2 = Test;
Console.WriteLine("1s:\t" + Equate(_1s_1, _1s_2));
// Test 1i (same type delegates from instance-method)
Program _1i_0 = new Program();
Compare<int> _1i_1 = _1i_0.Test3;
Compare<int> _1i_2 = _1i_0.Test3;
Console.WriteLine("1i:\t" + Equate(_1i_1, _1i_2));
// Test 2s (same type delegates from same type static-delegates)
Compare<int> _2s_1 = new Compare<int>(_1s_1);
Compare<int> _2s_2 = new Compare<int>(_1s_2);
Console.WriteLine("2s:\t" + Equate(_2s_1, _2s_2));
// Test 2i (same type delegates from same type instance-delegates)
Compare<int> _2i_1 = new Compare<int>(_1i_1);
Compare<int> _2i_2 = new Compare<int>(_1i_2);
Console.WriteLine("2i:\t" + Equate(_2i_1, _2i_2));
// Test 3s (different type delegates from static-method)
Compare<int> _3s_1 = Test;
Compare<int, int> _3s_2 = Test;
Console.WriteLine("3s:\t" + Equate(_3s_1, _3s_2));
// Test 3i (different type delegates from instance-method)
Program _3i_0 = new Program();
Compare<int> _3i_1 = _3i_0.Test3;
Compare<int, int> _3i_2 = _3i_0.Test3;
Console.WriteLine("3i:\t" + Equate(_3i_1, _3i_2));
// Test 4s (same type delegates from different type static-delegates)
Compare<int> _4s_1 = new Compare<int>(_3s_1);
Compare<int> _4s_2 = new Compare<int>(_3s_2);
Console.WriteLine("4s:\t" + Equate(_4s_1, _4s_2));
// Test 4i (same type delegates from different type instance-delegates)
Compare<int> _4i_1 = new Compare<int>(_3i_1);
Compare<int> _4i_2 = new Compare<int>(_3i_2);
Console.WriteLine("4i:\t" + Equate(_4i_1, _4i_2));
// Test 4s.1 (same type delegates from different type static-delegates)
Compare<int, int> _4s_1_1 = new Compare<int, int>(_3s_1);
Compare<int, int> _4s_1_2 = new Compare<int, int>(_3s_2);
Console.WriteLine("4s.1:\t" + Equate(_4s_1_1, _4s_1_2));
// Test 4i.1 (same type delegates from different type instance-delegates)
Compare<int, int> _4i_1_1 = new Compare<int, int>(_3i_1);
Compare<int, int> _4i_1_2 = new Compare<int, int>(_3i_2);
Console.WriteLine("4i.1:\t" + Equate(_4i_1_1, _4i_1_2));
// Test 5s (same type delegates from different static-methods with same IL compilations)
Compare<int> _5s_1 = Test;
Compare<int> _5s_2 = Test2;
Console.WriteLine("5s:\t" + Equate(_5s_1, _5s_2));
// Test 5i (same type delegates from different instance-methods with same IL compilations)
Program _5i_0 = new Program();
Compare<int> _5i_1 = _5i_0.Test3;
Compare<int> _5i_2 = _5i_0.Test4;
Console.WriteLine("5i:\t" + Equate(_5i_1, _5i_2));
Console.WriteLine();
Console.WriteLine("Enter to close...");
Console.ReadLine();
}
public static int Test(int l, int r) { return 0; }
public static int Test2(int l, int r) { return 0; }
public int Test3(int l, int r) { return 0; }
public int Test4(int l, int r) { return 0; }
// FIX ME!-----------------------------------------------------
public static bool Equate(System.Delegate a, System.Delegate b)
{
// standard equality
if (a == b)
return true;
// null
if (a == null || b == null)
return false;
// compiled method body
if (a.Target != b.Target)
return false;
byte[] a_body = a.Method.GetMethodBody().GetILAsByteArray();
byte[] b_body = b.Method.GetMethodBody().GetILAsByteArray();
if (a_body.Length != b_body.Length)
return false;
for (int i = 0; i < a_body.Length; i++)
{
if (a_body[i] != b_body[i])
return false;
}
return true;
}
}
}
Here are the tests that are currently failing:
2s, 2i, 4s, 4i, 4s.1, 4i.1

Here is the solution to these test cases. You have to remove all the overhead of cause by the delegate assignments. Just continually check if the target is a delegate.
public static bool Equate(System.Delegate a, System.Delegate b)
{
// ADDED THIS --------------
// remove delegate overhead
while (a.Target is Delegate)
a = a.Target as Delegate;
while (b.Target is Delegate)
b = b.Target as Delegate;
// standard equality
if (a == b)
return true;
// null
if (a == null || b == null)
return false;
// compiled method body
if (a.Target != b.Target)
return false;
byte[] a_body = a.Method.GetMethodBody().GetILAsByteArray();
byte[] b_body = b.Method.GetMethodBody().GetILAsByteArray();
if (a_body.Length != b_body.Length)
return false;
for (int i = 0; i < a_body.Length; i++)
{
if (a_body[i] != b_body[i])
return false;
}
return true;
}

So let's pull out the first failing test case and look at it in isolation. I'm going to change a few variable names for the sake of clarity.
Compare<int> firstComparer = Test;
Compare<int> secondComparer = Test;
Compare<int> thirdComparer = new Compare<int>(firstComparer);
Compare<int> fourthComparer = new Compare<int>(secondComparer);
Console.WriteLine("2s:\t" + Equate(thirdComparer, fourthComparer));
Now, when we get to the comparison, let's look at the target and method of each of these four delegates:
variable | Target | Method
firstComparer | null | Test
secondComparer | null | Test
thirdComparer | firstComparer | Invoke
fourthComparer | secondComparer | Invoke
Now, technically, the Target of thirdComparer isn't the variable firstComparer, the target is the value of that variable; it's the delegate that firstComparer is pointing to when you evaluate new Compare<int>(firstComparer), but hopefully you get the idea.
So why aren't the third and fourth delegates equal? Because they have completely different targets. These two delegates are both calling the same method of two different instances. Now it happens to be the case that those two completely different instances are going to do the same thing when you invoke them, in this particular case, but that doesn't necessarily need to be the case.
So if you want your equality to support this you'd somehow need to be able to determine if the target of the various delegates point to the same variable or point to equivalent values. Writing something that worked for this one case is probably doable; writing something that covered the general case is likely impossible. You can't necessarily know, in the general case, whether two objects should be considered "equivalent". You could pull out an IEqualityComparer<T>.Default, if that implementation would be sufficient. It would probably work here, but not all types override equality in a way you might want it to.
The best solution is likely to avoid this problem in the first place. Rather than having delegate's that, when invoked, invoke other delegates that are different, but that themselves point to the same value, you should just remove that layer of indirection. If we adjust the test case above to:
Compare<int> firstComparer = Test;
Compare<int> secondComparer = Test;
Compare<int> thirdComparer = firstComparer;
Compare<int> fourthComparer = secondComparer;
Console.WriteLine("2s:\t" + Equate(thirdComparer, fourthComparer));
Then the test passes, because the third and fourth delegates are different from each other, but are the same delegates pointed to by firstComparer and secondComparer, which both have the same target and method.
All of your other test cases, and not just this one test case, have exactly the same problem, so I don't see a reason to look at each one individually. All of them are adding this added layer of indirection, which is why they're not equal.

Related

How to check if class "is" an instance of a type given as a variable in C#?

I have the need to do a few checks like this:
if(thisInstance is ThatClass || thisInstance is ThatOtherClass)
I actually won't be needing to do a lot of these, so I could simply write it as it is above, but I'd still prefer doing this in a more tidy way, eg. like this:
Type[] arrayOfClasses = new Type[] { typeof(ThatClass), typeof(ThatOtherClass))}
if(isOfAnyType(thisInstance, arrayOfClasses))
But for some reason I can't get it to work. For some reason even though the string representations of
thisInstance.GetType()
and
typeof(ThatClass)
would be the same,
thisInstance.GetType().IsInstanceOfType(type)
will still always result in false.
In case it makes a difference, this is a Unity project, so if calling one further GetType() for the comparable items (eg. thisInstance.GetType().GetType()) the result is always System.MonoType, in which case the IsInstanceOfType always returns true, which of course isn't useful either.
Any ideas why the comparison fails or how to make it work? Or should I just give up and simply use the "thisInstance is ThatClass" wherever needed?
You could use something like this. The method checks a generic object whether it is of any of the types within the array and returns a bool.
public static bool IsOfAnyType<T>(T obj, Type[] types)
{
bool isOfAnyType = false;
for (int i = 0; i < classes.Length; i++)
{
if (types[i].IsAssignableFrom (obj.GetType()))
{
isOfAnyType = true;
break;
}
}
return isOfAnyType;
}
You can use isSubclass:
thisInstance.GetType().IsSubclassOf(typeof(ThatClass))
To get your desired way to work you could use this method:
public static bool IsOfAnyType(object obj, Type[] types)
{
return types.Any(type => type.IsInstanceOfType(obj));
}
If you can't use Linq you can write the method like this:
public static bool IsOfAnyType(object obj, Type[] types)
{
foreach (var type in types)
{
if (type.IsInstanceOfType(obj))
return true;
}
return false;
}
To test this I used this code:
Type[] typesToCheck = { typeof(ThatClass), typeof(ThatOtherClass) };
ThatClass input1 = new ThatClass();
ThatOtherClass input2 = new ThatOtherClass();
if (IsOfAnyType(input1, typesToCheck))
Console.WriteLine("Hello world from " + input1.GetType());
if (IsOfAnyType(input2, typesToCheck))
Console.WriteLine("Hello world from " + input2.GetType());

Atomically reading values of fields via reflection

Let's suppose I have the following C# declaration:
struct Counters
{
public long a;
public long b;
public long c;
}
Is this possible to iterate through the fields of a given instance of Counters and read their values using Interlocked.Read()? I.e.
Counters counters;
foreach (var counter in typeof(Counters).GetFields())
{
var value = Interlocked.Read(???);
}
You can't use Interlocked.Read directly, because it requires a ref argument - and you can't use the required System.Int64& type directly.
So, back to reflection:
// You can keep this static in some helper class
var method = typeof(Interlocked).GetMethod("Read", new []{ typeof(long).MakeByRefType() });
var result = (long)method.Invoke(null, new object[] { counter.GetValue(instance) });
EDIT: This doesn't work either, I botched up my testing. You're still reading a copy that wasn't produced atomically.
This will work, though:
public delegate long AtomicReadDelegate<T>(ref T instance);
public static AtomicReadDelegate<T> AtomicRead<T>(string name)
{
var dm = new DynamicMethod(typeof(T).Name + "``" + name + "``AtomicRead", typeof(long),
new [] { typeof(T).MakeByRefType() }, true);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldflda, typeof(T).GetField(name));
il.Emit(OpCodes.Call,
typeof(Interlocked).GetMethod("Read", new [] { typeof(long).MakeByRefType() }));
il.Emit(OpCodes.Ret);
return (AtomicReadDelegate<T>)dm.CreateDelegate(typeof(AtomicReadDelegate<T>));
}
private readonly AtomicReadDelegate<Counters>[] _allTheReads =
new []
{
AtomicRead<Counters>("a"),
AtomicRead<Counters>("b"),
AtomicRead<Counters>("c")
};
public static void SomeTest(ref Counters counters)
{
foreach (var fieldRead in _allTheReads)
{
var value = fieldRead(ref counters);
Console.WriteLine(value);
}
}
You might want to cache the delegates you get from AtomicRead - they can be reused safely. A simple concurrent dictionary will work just fine.
Don't forget that this only supports longs; you'll need to use Interlocked.CompareExchange if you need to atomically read other types as well (apart from references and ints, of course - though depending on your code, you might need some memory barriers even in that case).
Values for instance fields are per object, so you need to take the value for a particular object.
Counters counter1 = new Counter() { a = 40; b = 50; c = 60; }
Type counterType = counter1.GetType();
foreach (var field in counterType.GetFields())
{
var value = Interlocked.Read(field.GetValue(counter1));
}
In this case we get values for the fields of counter1 and not any other struct instance.
If you really need an atomic long, then it's better to use atomics.net library (which is available via NuGet).
If you need just read values of struct passed in your thread, then it's safe to read, since it's passed by value. But if it's passed by reference or if you work with unsafe/native code, then it's better to say what exactly you want to achieve.

Deep copying a Func within an object in C#

I am building a list of unit tests, which are organised as a list of objects, each of which contain the test method to be executed as a Func. Each object has a variable which is within scope of the Func and is used by it. The variable is not passed in as a parameter.
Iterating over the list and running all the tests runs fine, but is is possible to copy a Func from one object, -breaking the reference to that object-, and assign it to a new object? I assume this is possible somehow by creating a Deep Copy, but my attempt using BinaryFormatter has not worked, any tips would be appreciated!
I have a simplified forms application as follows to illustrate my problem:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing; using
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks; using System.Windows.Forms;
namespace WindowsFormsApplication4 {
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public static object DeepClone(object obj)
{
object objResult = null;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, obj);
ms.Position = 0;
objResult = bf.Deserialize(ms);
}
return objResult;
}
[Serializable]
public class POCOwithFunc {
public POCOwithFunc(Func<string> myfunc)
{
mqi = myfunc;
}
public POCOwithFunc() { }
public Func<string> mqi;
public object parm;
}
private void button1_Click(object sender, EventArgs e)
{
List<POCOwithFunc> testList = new List<POCOwithFunc>();
for (int x = 0; x < 5; x++)
{
var pc = new POCOwithFunc();
pc.parm = x;
pc.mqi = delegate()
{
var rrq = pc.parm;
return "result: " + pc.parm;
};
testList.Add(pc);
}
String output = "";
foreach (var test in testList)
{
output += test.mqi() + "\r\n";
}
//output:
//result: 0
//result: 1
//result: 2
//result: 3
//result: 4
var pocoToBeCopied = testList[2];
var newpoco = new POCOwithFunc();
newpoco.parm = 10;
newpoco.mqi = pocoToBeCopied.mqi;
var res = newpoco.mqi(); //returns 2
newpoco = (POCOwithFunc)DeepClone(pocoToBeCopied); //fails
}
} }
This is the first time I'm hearing about deep copy a delegate (which would not work, as the delegate (Func is type of delegate) contain reference to its closure (its environment, which contain any variable that that delegate is using).
I would suggest to change the parameter itself, or, to send it as a parameter (there is a delegate type for it too: Func<object, string >).
(and, I think you should think about redesign the whole thing :-/)
7 years later...
So I had encountered pretty much the same problem, also when writing tests (basically, I had a delegate that captured a lot of info from different parts of the program, and at the end of the program displayed it in a elaborate, error-prone way. I wanted to check that in all cases where captured variable turns to null/other weird values, the delegate handles it gracefully). I had a general idea that "no you can't" isn't completely true, so here goes.
TL;DR:
Closures: compiler generates a class that contains references to all
"captured" variables. This class can be accessed and edited via
reflection, thus "redirecting" the closure to point to another
variable.
Delegate target can also be edited via reflection.
To "deep copy" a delegate within an object you need to change
delegates' Target and (if needed) redirect any fields that point to
original object.
Long version:
Turned out, there is two questions here: first (and the one that was more important for me) - redirecting closures. When compiler detects a delegate that forms a closure that is moved beyond the current scope, a class is generated. This class contains references to the objects that were "captured", so that they don't get garbage collected despite being out of scope. (Also, turned out, the fields of this class are helpfully called by the name of variable that got captured.) You can see the generated class with Reflection:
<!-- language: csharp -->
public static void PrintClass(System.Delegate f)
{
var compilerGeneratedType = f.Target.GetType();
var fields = compilerGeneratedType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
Console.WriteLine(compilerGeneratedType.Name + " has fields:");
foreach (var field in fields)
{
Console.WriteLine(field.FieldType + "\t" + field.Name);
}
}
and the output when used on a classic closure example:
Action a = () => { };
for (int index = 0; index != 3; index++)
{
a += () => { Console.Write(index + " "); };
}
a(); //output: 3 3 3
PrintClass(a);
output of PrintClass function:
<>c__DisplayClass0_1 has fields: System.Int32 index
Modifying that closure can be done with reflection, like that:
static System.Delegate RedirectClosure(System.Delegate f, object newTarget, string originalVariableName)
{
System.Delegate result = (System.Delegate)f.Clone();
var compilerGeneratedType = f.Target.GetType();
var fields = compilerGeneratedType.GetFields();
foreach (var field in fields)
{
if (field.FieldType == newTarget.GetType() && field.Name == originalVariableName)
field.SetValue(result.Target, newTarget);
}
return result;
}
Using that earlier example:
Action a = () => { };
for (int index = 0; index != 3; index++)
{
a += () => { Console.Write(index + " "); };
}
a(); //output: 3 3 3
int j = 42;
RedirectClosure(a, (object)j, "index");
a(); //output: 42 42 42
Now for this specific example (delegate is inside a class, and it captured a field of this same class), second problem: the delegate's Target also needs to be changed. For this I modified the copying function to also redirect delegate's target. (Using .Clone() on delegate or straight-up copying it has the side effect: if I copy delegate from source to destination and modify only its "captured object" field to point to second destination - source delegate also starts pointing there. I guess they still share a reference to same Target.) Modified function (copies the delegate and redirects to new object):
static System.Delegate CopyDelegateAndRedirectClosure<T1>(System.Delegate f, T1 originalTarget, T1 newTarget)
{
System.Delegate result = (System.Delegate)f.Clone();
// I bet there is a better way to get a copy then this =(
var serialized = JsonSerializer.Serialize(result.Target);
var deserialized = JsonSerializer.Deserialize(serialized, result.Target.GetType());
var targetField = result.GetType().GetField("_target", BindingFlags.Instance | BindingFlags.NonPublic);
targetField.SetValue(result,deserialized);
var compilerGeneratedType = f.Target.GetType();
var fields = compilerGeneratedType.GetFields();
foreach (var field in fields)
{
if (field.FieldType == originalTarget.GetType() && field.GetValue(f.Target) == (object)originalTarget)
field.SetValue(result.Target, newTarget);
}
return result;
}
And here is an example of using it on the same situation the original poster had.
Test class:
class A
{
public int param;
public Func<int> del;
}
"Deep copying" the delegate from one instance to another:
var destination = new A { param = 10 };
var source = new A { param = 2 };
source.del = () => { return source.param + 100; };
Console.WriteLine($"source: param = {source.param}, del() result = {source.del()}");
// output:
// source: param = 2, del() result = 102
destination.del = (System.Func<int>)CopyDelegateAndRedirectClosure(source.del, source, destination);
Console.WriteLine($"destination: param = {destination.param}, del() result = {destination.del()}");
Console.WriteLine($"source: param = {source.param}, del() result = {source.del()}");
// output:
// destination: destination: param = 10, del() result = 110
// source: param = 2, del() result = 102
So there it is: copied the delegate from one instance to other, and it now operates on the new instance. The old one is unaffected.
Now again to "why would one do that" - yep, better architecture would have prevented me from ever finding this question. However, this did allow me to write a test replacing all captured variables of a specific type with "broken" value, and "bad architecture + tests" is better then "bad architecture + no tests".

Syntax for mocking a method with a ref argument

I have the problem that I can't mock a method that has a ref argument.
The signature of the method I want to mock away is as follows:
class ContractRepository
...
public long GetValueAndIncrement(ref Counter counter)
{
...
}
I tried to mock it like this:
Random myRandomizer = new Random();
var contractRepo = new SIContractRepository();
contractRepo.GetValueAndIncrementCounterRef = ((internalCounter) => Int64.Parse(myRandomizer.Next().ToString()));
But the compiler tells me that I am missing the "ref" keyword, but when I try it like this
Random myRandomizer = new Random();
var contractRepo = new SIContractRepository();
contractRepo.GetValueAndIncrementCounterRef = ((ref internalCounter) => Int64.Parse(myRandomizer.Next().ToString()));
I get an error that ref is an invalid expression
Unfortunately, google doesn't help here. :(
Any ideas?
You simply can't use anonymous methods in this case, because they support neither ref nor out parameters. You need to create a "real" method.
public void SetupMock()
{
Random myRandomizer = new Random();
var contractRepo = new SIContractRepository();
contractRepo.GetValueAndIncrementCounterRef = GetValueAndIncrementMock;
}
public long GetValueAndIncrementMock(ref Counter counter)
{
return Int64.Parse(myRandomizer.Next().ToString())
}
You can use anonymous methods with the ref keyword, just explicitly specify the type in the anonymous method:
(ref Counter internalCounter) => Int64.Parse(myRandomizer.Next().ToString())
Remember that the current version of Moles only supports ref and out arguments as the LAST argument of a method.
http://research.microsoft.com/en-us/projects/pex/molesmanual.pdf
Limitations
The current implementation of Moles has several limitations. These limitations are not
inherent to the approach and might be resolved in future releases of Moles:
 The Moles framework supports only a limited number of method signature—up to
10 arguments, where the last argument can be an out or ref argument.
Method signatures with pointers are not supported.
 Sealed classes or static methods cannot be stubbed because stub types rely on
virtual method dispatch. For such cases, use mole types as described in “Mole
Types” later in this document
I am not sure whether this is a correct way to apply moles but I did it. And it works.
///method get call in unit test
public static void DetermineSprintCorporateLiableCustomer()
{
COptions p2 = new COptions();
MGetCOptions.CustomerInfoCallerOptionsRef = (ref COptions p1) =>
{
if (p1 != null && p1 != null && p1.Type.Equals(
"Data", StringComparison.OrdinalIgnoreCase))
{
p1.Type = "P";
p1.Indicator = true;
}
p2 = p1;
};
}
When this part executed during test run, new p2 is available. Below was my scenario.
// need to unit test Coptions.Type="Data"
public static MainMethod(Coptions)
{
Mclass.Method(ref Coptions);
If(COptions.Type="B")
Do something();
}
It works with new value but there could be a better way.

eval(string) to C# code

Is it possible to evaluate the following in C# at runtime
I have a class that contains 3 properties (Field,Operator,Value)
rule.Field;
rule.Operator;
rule.Value;
this is my rule class...
Now I have a loop
foreach(item in items)
{
// here I want to create a dynamic expression to evaluate at runtime
// something like
if (item.[rule.field] [rule.operator] [rule.value])
{ do work }
}
I just don't know the syntax, or if its possible in C#, I know in JS its possible but that's not a compiled language.
Update
Essentially I want a way to eval(stringCode) or a better more supported way.
No, C# doesn't support anything like this directly.
The closest options are:
Create a full valid C# program and dynamically compile it with CSharpCodeProvider.
Build an expression tree, compile and execute it
Perform the evaluation yourself (this may actually be easiest, depending on your operators etc)
Disclaimer: I'm the owner of the project Eval Expression.NET
This library is close to being the JS Eval equivalent. You can almost evaluate and compile all the C# language.
Here is a simple example using your question, but the library goes way beyond this simple scenario.
int field = 2;
int value = 1;
string binaryOperator = ">";
string formula = "x " + binaryOperator + " y";
// For single evaluation
var value1 = Eval.Execute<bool>(formula, new { x = field, y = value });
// For many evaluation
var compiled = Eval.Compile<Func<int, int, bool>>(formula, "x", "y");
var value2 = compiled(field, value);
EDIT Answer comment:
Proprietary library to do simple evaluation? No, thanks
This library does not support only simple evaluation but almost all the C# languages. Allowing you to add dynamically a method, use async, linq, loop, etc., which is more than "to do simple evaluation"
The closest options solution provided by Jon Skeet are great but will surely take several days of development and testing to support all cases, depending on the operators. Surely this library helps some developers, but in some other scenarios, like yours, it could be done without it.
I'm not entirely sure what you are saying. Can you try clarifying it a bit?
Are you wanting to to take a string expression and evaluate it at runtime in C#? If so the answer is no. C# does not support such types of dynamic evaluation.
You'd have to either use the CodeDOM libraries or create an Expression tree, compile it, and execute it. I think building up the expression tree is the best option.
Of course you could put in a switch statement on your operator, which is not bad because there is a limited number of operators you could use anyways.
Here's a way to do this with expression trees (written in LINQPad):
void Main()
{
var programmers = new List<Programmer>{
new Programmer { Name = "Turing", Number = Math.E},
new Programmer { Name = "Babbage", Number = Math.PI},
new Programmer { Name = "Lovelace", Number = Math.E}};
var rule0 = new Rule<string>() { Field = "Name", Operator = BinaryExpression.Equal, Value = "Turing" };
var rule1 = new Rule<double>() { Field = "Number", Operator = BinaryExpression.GreaterThan, Value = 2.719 };
var matched0 = RunRule<Programmer, string>(programmers, rule0);
matched0.Dump();
var matched1 = RunRule<Programmer, double>(programmers, rule1);
matched1.Dump();
var matchedBoth = matched0.Intersect(matched1);
matchedBoth.Dump();
var matchedEither = matched0.Union(matched1);
matchedEither.Dump();
}
public IEnumerable<T> RunRule<T, V>(IEnumerable<T> foos, Rule<V> rule) {
var fieldParam = Expression.Parameter(typeof(T), "f");
var fieldProp = Expression.Property (fieldParam, rule.Field);
var valueParam = Expression.Parameter(typeof(V), "v");
BinaryExpression binaryExpr = rule.Operator(fieldProp, valueParam);
var lambda = Expression.Lambda<Func<T, V, bool>>(binaryExpr, fieldParam, valueParam);
var func = lambda.Compile();
foreach(var foo in foos) {
var result = func(foo, rule.Value);
if(result)
yield return foo;
}
}
public class Rule<T> {
public string Field { get; set; }
public Func<Expression, Expression, BinaryExpression> Operator { get; set; }
public T Value { get; set; }
}
public class Programmer {
public string Name { get; set; }
public double Number { get; set; }
}
A better design for you would be for your rule to apply the test itself (or to an arbitrary value)
By doing this with Func instances you will get the most flexibility, like so:
IEnumerable<Func<T,bool> tests; // defined somehow at runtime
foreach (var item in items)
{
foreach (var test in tests)
{
if (test(item))
{
//do work with item
}
}
}
then your specific test would be something like this for strong type checking at compile time:
public Func<T,bool> FooEqualsX<T,V>(V x)
{
return t => EqualityComparer<V>.Default.Equals(t.Foo, x);
}
For a reflective form
public Func<T,bool> MakeTest<T,V>(string name, string op, V value)
{
Func<T,V> getter;
var f = typeof(T).GetField(name);
if (f != null)
{
if (!typeof(V).IsAssignableFrom(f.FieldType))
throw new ArgumentException(name +" incompatible with "+ typeof(V));
getter= x => (V)f.GetValue(x);
}
else
{
var p = typeof(T).GetProperty(name);
if (p == null)
throw new ArgumentException("No "+ name +" on "+ typeof(T));
if (!typeof(V).IsAssignableFrom(p.PropertyType))
throw new ArgumentException(name +" incompatible with "+ typeof(V));
getter= x => (V)p.GetValue(x, null);
}
switch (op)
{
case "==":
return t => EqualityComparer<V>.Default.Equals(getter(t), value);
case "!=":
return t => !EqualityComparer<V>.Default.Equals(getter(t), value);
case ">":
return t => Comparer<V>.Default.Compare(getter(t), value) > 0;
// fill in the banks as you need to
default:
throw new ArgumentException("unrecognised operator '"+ op +"'");
}
}
If you wanted to be really introspective and handle any literal without knowing at compile time you could use the CSharpCodeProvider to compile a function assuming something like:
public static bool Check(T t)
{
// your code inserted here
}
This is of course a massive security hole so whoever can supply code for this must be fully trusted. Here is a somewhat limited implementation for your specific needs (no sanity checking at all)
private Func<T,bool> Make<T>(string name, string op, string value)
{
var foo = new Microsoft.CSharp.CSharpCodeProvider()
.CompileAssemblyFromSource(
new CompilerParameters(),
new[] { "public class Foo { public static bool Eval("+
typeof(T).FullName +" t) { return t."+
name +" "+ op +" "+ value
+"; } }" }).CompiledAssembly.GetType("Foo");
return t => (bool)foo.InvokeMember("Eval",
BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod ,
null, null, new object[] { t });
}
// use like so:
var f = Make<string>("Length", ">", "2");
For this to work with arbitrary types you would have to do a bit more reflection to find the target assembly for the type to reference it in the compiler parameters.
private bool Eval(object item, string name, string op, string value)
{
var foo = new Microsoft.CSharp.CSharpCodeProvider()
.CompileAssemblyFromSource(
new CompilerParameters(),
new[] { "public class Foo { public static bool Eval("+
item.GetType().FullName +" t) "+
"{ return t."+ name +" "+ op +" "+ value +"; } }"
}).CompiledAssembly.GetType("Foo");
return (bool)foo.InvokeMember("Eval",
BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod ,
null, null, new object[] { item });
}
All the above code is simply a proof of concept, it lacks sanity checking and has serious performance issues.
If you wanted to be even fancier you could use Reflection.Emit with DynamicMethod instances to do it (using proper operators rather than the default comparer instances) but this would require complex handling for types with overridden operators.
By making your check code highly generic you may include more tests in future as you need to. Essentially isolate the part of your code that cares only about a function from t -> true/false from the code that supplies these functions.
CSharpCodeProvider; switch statements that pick the proper different "operators"; the DLR... they are all ways you could do this; but they seem weird solutions to me.
How about just using delegates?
Assuming your Field and Value are numbers, declare something like this:
delegate bool MyOperationDelegate(decimal left, decimal right);
...
class Rule {
decimal Field;
decimal Value;
MyOperationDelegate Operator;
}
Now you can define your 'rule' as, for example, a bunch of lambdas:
Rule rule1 = new Rule;
rule1.Operation = (decimal l, decimal r) => { return l > r; };
rule1.Field = ...
You can make arrays of rules and apply them whichever way you wish.
IEnumerable<Rule> items = ...;
foreach(item in items)
{
if (item.Operator(item.Field, item.Value))
{ /* do work */ }
}
If Field and Values are not numbers, or the type depends on the specific rule, you can use object instead of decimal, and with a little bit of casting you can make it all work.
That's not a final design; it's just to give you some ideas (for example, you would likely have the class evaluate the delegate on its own via a Check() method or something).
You can retrieve the field by reflection. And then implement the operators as methods and uses reflection or some types of enum-delegate mapping to call the operators. The operators should have at least 2 parameters, the input value and the value you are using to test against with.
While it is true that you probably won't find an elegant way to evaluate full C# code on the fly without the use of dynamically compiling code (which is never pretty), you can almost certainly get your rules evaluated in short order using either the DLR (IronPython, IronRuby, etc) or an expression evaluator library that parses and executes a custom syntax. There is one, Script.NET, that provides a very similar syntax to C#.
Take a look here:Evaluating Expressions a Runtime in .NET(C#)
If you have the time / inclination to learn a little Python, then IronPython and the DLR will solve all your issues:
Extending your App with IronPython

Categories