Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Core and type equality

Tags:

.net-core

This test:

[Fact]
public void X()
{
    Assert.IsType<Expression<Func<int>>>(Expression.Lambda<Func<int>>(Expression.Constant(1)));
}

Runs on .NET Framework, but fails on .NET Core (3.1 in my case) with the following error:

Expected: System.Linq.Expressions.Expression`1[[System.Func`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
Actual:   System.Linq.Expressions.Expression0`1[[System.Func`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]

Why is this? And can I do something to compare the types properly?

like image 842
asgerhallas Avatar asked Oct 16 '22 05:10

asgerhallas


1 Answers

Incorrect version of my answer before:

Assuming the behavior for .Net Core is the same or similar to framework, here is my reasoning.

Looking at the reference source code Expression.Lambda<T> creates a runtime type of Expression<T>, see typeof(Expression<>).MakeGenericType.

typeof(Expression<T>) however is the compile time type.

Using the following LINQPad script you see the difference in the output

var exp = Expression.Lambda<Func<Int32>>(Expression.Constant(1));
exp.GetType().BaseType.Dump();
// System.Linq.Expressions.Expression`1[System.Func`1[System.Int32]]
typeof(Expression<Func<Int32>>).BaseType.Dump();
// System.Linq.Expressions.LambdaExpression

For .Net Framework an instance of Expression<T> is returned from Expression.Lambda<T>(Expression exp) as expected.

But for .Net Core the behavior was changed!

For .Net Core either Expression<TDelegate>.Create or ExpressionCreator<TDelegate>.CreateExpressionFunc is called along the way but in both cases - for this example here - a new instance of Expression0{T} : Expression{T} is returned! Which are obviously not of the same type.

to assert your type you may use

var expression = Expression.Lambda<Func<int>>(Expression.Constant(1));
// extract the type of used generics
var genericArgs = expression.GetType().GetGenericArguments();
// TODO: assert that we DO have exactly one argument here
Assert.IsType<Func<int>>(genericArgs[0]);

// another assert:
// unverified, but `(expression is Expression<Func<int>> _).Dump();` returns `true` in LINQPad
Assert.True(expression is Expression<Func<int>>);
like image 85
ckerth Avatar answered Oct 21 '22 03:10

ckerth