views:

219

answers:

2

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
+1  A: 

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))
Jon Skeet
A: 

Yes that hack did the trick thank you so much for taking a look. ps. i have your book 'c# in depth' and could not recommend it more highly.

PhilHoy
Cool, thanks :) Feel free to write an Amazon review ;)
Jon Skeet