I've got an extension method that used to take a strongly typed Expression<Func<>>
parameter however for implementation reasons I had to change it to use a weakly type version. This has had a strange affect on the expression parameter as it now seems to be wrapping the lambda expression in an explicit call to a 'Convert' method.
Previously the paramters would look like:
m => m.Data
And now it looks like the following:
m => Convert(m.Data)
I have replicated the issue with the following example code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace ConsoleApplication
{
static class Program
{
static void Main(string[] args)
{
Model model = new Model()
{
Data = 123
};
Test(m => m.Data, m => m.Data);
Console.ReadLine();
}
public static void Test<TProperty>(Expression<Func<Model, TProperty>> strongTyped, Expression<Func<Model, object>> weakTyped)
{
Console.WriteLine("Strong Typed: {0}", strongTyped);
Console.WriteLine("Weak Typed: {0}", weakTyped);
}
}
public class Model
{
public int Data
{
get;
set;
}
}
}
The output of which is as follows:
Strong Typed: m => m.Data
Weak Typed: m => Convert(m.Data)
I am guessing it has something to do with auto boxing the value type into an object type. Can anyone confirm this or does anyone know what is going on? Also does anyone know where the Convert method is declared?
Calling the compile method on the weak typed expression gives the following:
weakTyped.Compile().Method
{System.Object lambda_method(System.Runtime.CompilerServices.Closure, ConsoleApplication.Model)}
[System.Reflection.Emit.DynamicMethod.RTDynamicMethod]: {System.Object lambda_method(System.Runtime.CompilerServices.Closure, ConsoleApplication.Model)}
base {System.Reflection.MethodBase}: {System.Object lambda_method(System.Runtime.CompilerServices.Closure, ConsoleApplication.Model)}
MemberType: Method
ReturnParameter: null
ReturnType: {Name = "Object" FullName = "System.Object"}
ReturnTypeCustomAttributes: {System.Reflection.Emit.DynamicMethod.RTDynamicMethod.EmptyCAHolder}
Since a lambda expression is just another way of specifying a delegate, we should be able to rewrite the above sample to use a lambda expression instead of an anonymous delegate. In the preceding example, the lambda expression used is i => i % 2 == 0 . Again, it is just a convenient syntax for using delegates.
They are actually two very different things. "Delegate" is actually the name for a variable that holds a reference to a method or a lambda, and a lambda is a method without a permanent name. Lambdas are very much like other methods, except for a couple subtle differences.
Lambda expressions in C# are used like anonymous functions, with the difference that in Lambda expressions you don't need to specify the type of the value that you input thus making it more flexible to use. The '=>' is the lambda operator which is used in all lambda expressions.
Which is NOT true about lambda statements? A statement lambda cannot return a value.
Convert
isn't a method at all - it's a UnaryExpression and indeed is there exactly why you theorized - boxing / type coercion. It's interesting to see when expression trees are generated, things we generally know are implicit actually appear explicitly.
If you were building an expression yourself, you can get the same effect by calling Expression.Convert()
:
Creates a UnaryExpression that represents a type conversion operation.
Yes, it is Conversion expression which represents boxing and not only boxing. It contains user defined conversions etc.
For example, If a type defines user defined conversions that will be convered using "Conversion expression". In this case weakTyped.Body.Method
will return the overloaded method something like op_Implicit...
You can prove this with following code.
public static void Test<TProperty>(Expression<Func<Model, TProperty>> strongTyped, Expression<Func<Model, object>> weakTyped)
{
var expr = (UnaryExpression)weakTyped.Body;
Console.WriteLine("Weak Typed method: {0}", expr.Method);
Console.WriteLine("Strong Typed: {0}", strongTyped);
Console.WriteLine("Weak Typed: {0}", weakTyped);
}
public static void TestFloat<TProperty>(Expression<Func<Model, TProperty>> strongTyped, Expression<Func<Model, decimal>> weakTyped)
{
var expr = (UnaryExpression) weakTyped.Body;
Console.WriteLine("Weak Typed method: {0}", expr.Method);
Console.WriteLine("Strong Typed: {0}", strongTyped);
Console.WriteLine("Weak Typed: {0}", weakTyped);
}
For decimal type this returns overloaded operator where as object
weakTyped.Body.Method
will be null since it is just a boxing conversion.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With