Reputation: 1613
The code below gives an error:
Property 'Int32 Key' is not defined for type 'ConsoleApplication1.IKeyed`1[TKey]'when the expression e is created but is fine when func f is created, can anyone explain why and if there is a way to fix it?
Module Module1
Sub Main()
Dim g = New keyedThingGetter(Of KeyedThing, Integer)
Dim thing = g.getThing()
End Sub
End Module
Public Class keyedThingGetter(Of Tthing As IKeyed(Of TKey), TKey)
Public Function getThing() As Tthing
Dim f As Func(Of Tthing, Boolean)
f = Function(thing) thing.Key.Equals(1)
Dim e As Expressions.Expression(Of Func(Of Tthing, Boolean))
e = Function(thing) thing.Key.Equals(1)
Return Nothing
End Function
End Class
Public Interface IKeyed(Of TKey)
ReadOnly Property Key() As TKey
End Interface
Public Class KeyedThing
Implements IKeyed(Of Integer)
Public ReadOnly Property Key() As Integer Implements IKeyed(Of Integer).Key
Get
Return 1
End Get
End Property
End Class
Upvotes: 0
Views: 361
Reputation: 1504182
Workaround is at the bottom
That's very odd. I'm still looking into it, but this "mostly equivalent" C# works fine:
using System;
using System.Linq.Expressions;
interface IKeyed<TKey>
{
TKey Key { get; }
}
class KeyedThing : IKeyed<int>
{
public int Key { get { return 1; } }
}
class KeyedThingGetter<TThing, TKey> where TThing : IKeyed<TKey>
{
public void GetThing()
{
Func<TThing, bool> f = thing => thing.Key.Equals(1);
Expression<Func<TThing, bool>> e = thing => thing.Key.Equals(1);
}
}
class Test
{
static void Main()
{
var g = new KeyedThingGetter<KeyedThing, int>();
g.GetThing();
}
}
EDIT: There's an interesting difference between the expression trees created. Here's the VB expression (decompiled to C# with Reflector):
Expression<Func<Tthing, bool>> expression = Expression
.Lambda<Func<Tthing, bool>> (Expression.Call(Expression.Convert
(Expression.Property(Expression.Convert(expression2 =
Expression.Parameter(typeof(Tthing), "thing"), typeof(IKeyed<>)),
(MethodInfo) methodof(IKeyed<TKey>.get_Key, IKeyed<TKey>)), typeof(object)),
(MethodInfo) methodof(object.Equals), new Expression[] {
Expression.Convert(Expression.Constant(1, typeof(int)), typeof(object)) }), new
ParameterExpression[] { expression2 });
Here's the C# version:
Expression<Func<TThing, bool>> expression = Expression
.Lambda<Func<TThing, bool>> (Expression.Call(Expression.Convert
(Expression.Property(Expression.Convert(expression2 =
Expression.Parameter(typeof(TThing), "thing"), typeof(IKeyed<TKey>)),
(MethodInfo) methodof(IKeyed<TKey>.get_Key, IKeyed<TKey>)), typeof(object)),
(MethodInfo) methodof(object.Equals), new Expression[] {
Expression.Convert(Expression.Constant(1, typeof(int)), typeof(object)) }), new
ParameterExpression[] { expression2 });
The difference is in the fourthline - the type of the parameter. In the C#, it's typeof(IKeyed<TKey>)
whereas in the VB it's typeof(IKeyed<>)
.
A bug in the VB compiler perhaps? Not sure yet. Hopefully Marc G will chime in soon, as resident expression tree expert...
EDIT: Given the difference, I worked out how to fix it. Either change it to:
Dim e as Expressions.Expression(Of Func(Of Tthing, Boolean))
e = Function(thing as IKeyed(Of TKey)) thing.Key.Equals(1))
or
Dim e as Expressions.Expression(Of Func(Of IKeyed(Of TKey), Boolean))
e = Function(thing) thing.Key.Equals(1))
Upvotes: 1