Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
419 views
in Technique[技术] by (71.8m points)

C# Use Expression to get value from an object

the object cloud be any class, and I don't want to use reflection. this is my code:

class Dog
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }

        static void test()
        {
            object d = new Dog
            {
                Id = 21,
                Name = "adsf"
            };
            genericeTest(d);
        }

        static void genericeTest<T>(T t)
        {
            var type = t.GetType();
            var arg = Expression.Parameter(type, "x");
            var expr = Expression.Property(arg, "Id");
            var compiled = Expression.Lambda<Func<T, int>>(expr, arg).Compile();
            var value = compiled.Invoke(t);
        }

but there is exception System.ArgumentException: ParameterExpression of type 'ExpressionDemo.Program+Dog' cannot be used for delegate parameter of type 'System.Object' at var compiled = Expression.Lambda<Func<T, int>>(expr, arg).Compile();

if I write like this

   var compiled = Expression.Lambda<Func<Dog, int>>(expr, arg).Compile();
   var value = compiled.Invoke(t as Dog);

it works well. can I fix it, thank you.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem is that your T is an object, because d has a compile-time type of object, but t.GetType() is Dog, because the runtime type of t is Dog.

So you've got an expression which accepts a Dog, but you're trying to pass it a variable of type object -- you're missing the cast which would turn the object into a Dog.

In other words, you're writing something roughly equivalent to:

public int GeneratedMethod(object o)
{
    Dog d = o; // <-- This line will cause a compile-time error, of course
    return d.Id;
}

The question is, what do you want your generated method to look like? Should it look like this?

public int GeneratedMethod(object o)
{
    Dog d = (Dog)o;
    return d.Id;
}

If so, you'll need to insert the cast from object to Dog. The way to do this is using Expression.Convert:

var type = t.GetType();
var arg = Expression.Parameter(typeof(T), "x");
var expr = Expression.Property(Expression.Convert(arg, type), "Id");
var compiled = Expression.Lambda<Func<T, int>>(expr, arg).Compile();

Here we're declaring a lambda which accepts a T, but then casts it to Dog.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...