📜 ⬆️ ⬇️

Expressions in C # - impress yourself!

.NET 4.0 is just around the corner and will bring a bunch of everything new, necessary and not very cool and super cool. However, in the good old .NET 3.5 there are many different interesting features that are not used in everyday work, but sometimes they make life easier for developers. One of these great pieces is Expressions.



Expressions (or, more correctly, expression trees) is nothing but an expression tree, familiar from university times, when you just learned to program. Here is a napyrmer, a tree for expressing 2 + 3 * 4.


System.Linq.Expressions is, in principle, the same trees, only larger and more complex (there are 56 different expressions in .NET, starting with simple mathematic expressions, ending with list initialization and calling methods).
')
Expressions can be built in two ways - in compile-time, and in run-time. In compile-time, the compiler will automatically parse our code and assemble Expression from it. For example, for this line:
Expression<Func< string , int >> ex = s => s.Replace( "x" , "yy" ).Length*2;

* This source code was highlighted with Source Code Highlighter .

the compiler will actually issue the following code:
ParameterExpression CS$0$0000;
Expression<Func< string , int >> ex = Expression.Lambda<Func< string , int >>(Expression.Multiply(Expression.Property(Expression.Call(CS$0$0000 = Expression.Parameter( typeof ( string ), "s" ), (MethodInfo) methodof( string .Replace), new Expression[] { Expression.Constant( "x" , typeof ( string )), Expression.Constant( "yy" , typeof ( string )) }), (MethodInfo) methodof( string .get_Length)), Expression.Constant(2, typeof ( int ))), new ParameterExpression[] { CS$0$0000 });

* This source code was highlighted with Source Code Highlighter .

Thus, we get access to the internal structure of the expression and we can match it to some external language - in particular, LINQ to SQL works in this way, which parses the resulting expression (I said that the LINQ query is actually a call chain and is also translated into ?) and executes the corresponding T-SQL query. I will not tell you how this is done - this requires a new article.


I want to tell you about the second way to build expressions - in run-time. For example, we are faced with the task of writing a deserializer that will collect an object based on a set of “property name”: “value” pairs. The standard way to solve the problem is reflection, which is known to be very slow. Here is the code of the creator through reflection (I will not explain, everything should be clear):
internal class ReflectionCreator<T>:ICreator<T>
{
private readonly List <PropertyInfo> _infos;

public ReflectionCreator()
{
_infos = new List <PropertyInfo>( typeof (T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty));
}
public T Create(Dictionary< string , object > props)
{
var newObject = Activator.CreateInstance<T>();
foreach ( var propertyInfo in _infos)
{
object value ;
if (props.TryGetValue(propertyInfo.Name, out value ))
{
propertyInfo.SetValue(newObject, value , null );
}
}
return newObject;
}
}

* This source code was highlighted with Source Code Highlighter .

This creator spends 0.001 seconds to create a creator and 1.20 seconds to create 10,000 objects (yes, creating a creator ...). Long, but not fatal. But let's take a look at the expressions.

Consider the problem closer. If we were faced with the task of writing a deserializer for a particular class, for example, for the Foo class:
class Foo
{
public string Name { get ; set ; }
public int Value { get ; set ; }
}


* This source code was highlighted with Source Code Highlighter .

we could just construct the necessary functor, like this:
Func<Dictionary< string , object >, Foo> fooCreator =
d => new Foo
{
Name = d.GetValue< string >( "Name" ),
Value = d.GetValue< int >( "Value" )
};

* This source code was highlighted with Source Code Highlighter .

Where GetValue () is an extension method for a dictionary:
static class DictionaryExtension
{
public static TType GetValue<TType>( this Dictionary< string , object > d, string name)
{
object value ;
return d.TryGetValue(name, out value ) ? (TType) value : default (TType);
}
}


* This source code was highlighted with Source Code Highlighter .



We already know that, in principle, any piece of code can be represented as an expression tree simply by replacing Func <> with Expression <Func <>>. Let's take a look at what the compiler will show us when we wrap this functor in an expression (I apologize for the sheet, but it's hard to explain without it):
  1. ParameterExpression CS0 = Expression.Parameter ( typeof (Dictionary < string , object >), "d" );
  2. var fooCreator = Expression.Lambda <Func <Dictionary < string , object >, Foo >>
  3. (
  4. Expression.MemberInit
  5. (
  6. Expression.New
  7. (
  8. (ConstructorInfo) methodof (Foo..ctor),
  9. new Expression [0]
  10. ),
  11. new MemberBinding []
  12. {
  13. Expression.Bind
  14. (
  15. (MethodInfo) methodof (Foo.set_Name),
  16. Expression.Call
  17. (
  18. null
  19. (MethodInfo) methodof (DictionaryExtension.GetValue),
  20. new Expression []
  21. {
  22. CS0,
  23. Expression.Constant ( "Name" , typeof ( string ))
  24. }
  25. )
  26. ),
  27. Expression.Bind
  28. (
  29. (MethodInfo) methodof (Foo.set_Value),
  30. Expression.Call
  31. (
  32. null
  33. (MethodInfo) methodof (DictionaryExtension.GetValue),
  34. new Expression []
  35. {
  36. CS0,
  37. Expression.Constant ( "Value" , typeof ( string ))
  38. }
  39. )
  40. )
  41. }
  42. ),
  43. new ParameterExpression [] {CS0}
  44. );
* This source code was highlighted with Source Code Highlighter .



Let's try to figure it out.
1. The parameter for our expression (which is logical, because our fuctor also accepts one parameter).
2. Our expression is a lambda expression from the functor.
4. We initialize class members
6-10. ... calling the constructor without parameters
11. ... and with the following set of initializers:
13-15. Foo.name
16. ... whose value is obtained as the result of a method call
18. ... static
19. ... DictionaryExtension.GetValue (note that calling the extension-method is no different from calling the static method)
20. ... with parameters
22. ... d, which will be passed to us as a parameter of the functor
23. ... and the string constant parameter "Name", which is nothing but the name of the property
27-40. All the same for Foo.Value
43. Actually, the parameter for the functor itself.


I think it is quite understandable that we can repeat this code, only in place of the MemberBindings [] hard-code array, substitute our own, assembled for this type. After this, we will only have to somehow execute this expression. To do this, LambdaExpression has a great Compile () method that compiles our functor from this expression.


Here is the code of our new creator:
class ExpressionCreator<T> : ICreator<T>
{
private readonly Func<Dictionary< string , object >, T> _creator;

public ExpressionCreator()
{
var type = typeof (T);
var newExpression = Expression.New(type);
var dictParam = Expression.Parameter( typeof (Dictionary< string , object >), "d" );
var list = new List <MemberBinding>();
var propertyInfos = type.GetProperties(BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.SetProperty);
foreach ( var propertyInfo in propertyInfos)
{
Expression call = Expression.Call(
typeof (DictionaryExtension),
"GetValue" , new [] {propertyInfo.PropertyType},
new Expression[]
{
dictParam,
Expression.Constant(propertyInfo.Name)
});

MemberBinding mb = Expression.Bind(propertyInfo.GetSetMethod(), call);
list.Add(mb);
}

var ex = Expression.Lambda<Func<Dictionary< string , object >, T>>(
Expression.MemberInit(newExpression, list),
new [] {dictParam});
_creator = ex.Compile();
}
public T Create(Dictionary< string , object > props)
{
return _creator(props);
}
}

* This source code was highlighted with Source Code Highlighter .

We, in fact, repeat the code created by the compiler, but dynamically process all the properties of any given object. (The construction is completely analogous to the one above).


The new creator creates 0.01 second (which is 10 times slower than that of reflection, but the designer is called only once) and spends 0.017 seconds to create 10,000 objects (which is 70 times faster).


By the way, if you create a foo object directly
internal class DirectCreator : ICreator<Foo>
{
public Foo Create(Dictionary< string , object > props)
{
return new Foo
{
Name = props.GetValue< string >( "Name" ),
Value = props.GetValue< int >( "Value" )
};
}
}

* This source code was highlighted with Source Code Highlighter .

then it turns out just two times faster than through expressions.


Here such things allow us to do Expression trees.


The source code can be found here: tyts .


Source: https://habr.com/ru/post/83169/


All Articles