当类型仅在运行时已知时,如何使用表达式树调用泛型方法?

发布于 2024-08-25 10:26:15 字数 432 浏览 2 评论 0原文

这是我使用反射解决的问题,但想看看如何使用表达式树来解决这个问题。

我有一个通用函数:

private void DoSomeThing<T>( param object[] args ) {
    // Some work is done here.
}

我需要从班级的其他地方调用它。现在,通常情况下,这很简单:

DoSomeThing<int>( blah );

但前提是我在设计时知道我正在使用 int。当我直到运行时才知道类型时,我需要帮助。就像我说的,我知道如何通过反射来做到这一点,但我想通过表达式树来做到这一点,因为我(非常有限)的理解是我可以这样做。

有什么建议或指向可以让我了解这一点的网站吗?最好有示例代码?

This is something that I solved using reflection, but would like to see how to do it using expression trees.

I have a generic function:

private void DoSomeThing<T>( param object[] args ) {
    // Some work is done here.
}

that I need to call from else where in my class. Now, normally, this would be be simple:

DoSomeThing<int>( blah );

but only if I know, at design time that I am working with an int. When I do not know the type until runtime is where I need the help. Like I said, I know how to do it via reflection, but I would like to do it via expression trees, as my (very limited) understanding is that I can do so.

Any suggestions or points to sites where I can get this understanding, preferably with sample code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蘸点软妹酱 2024-09-01 10:26:15

是的,可以通过表达式树来完成。优点是您可以获得委托,因此重复调用将比一遍又一遍地执行 MethodInfo.Invoke() 快得多。 dynamic 关键字也可以做到这一点。

示例:

What type would you like to use?
decimal
Selected type 'System.Decimal'
Input Value:
5.47
<<<USING object>>>
The object has static type 'System.Object',  dynamic type 'System.Decimal', and value '5.47'
<<<USING dynamic>>>
The object has static type 'System.Decimal',  dynamic type 'System.Decimal', and value '5.47'
<<<USING reflection>>>
The object has static type 'System.Decimal',  dynamic type 'System.Decimal', and value '5.47'
<<<USING expression tree>>>
The object has static type 'System.Decimal',  dynamic type 'System.Decimal', and value '5.47'

代码:

using System;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace SO2433436
{
    class Program
    {
        static void LogObject<T>(T t)
        {
            Console.WriteLine("The object has static type '" + typeof(T).FullName + "',  dynamic type '" + t.GetType() + "', and value '" + t.ToString() + "'");
        }

        static void Main(string[] args)
        {
            Console.WriteLine("What type would you like to use?");
            string typeName = Console.ReadLine();

            Type userType;
            switch (typeName)
            {
                case "byte": userType = typeof(byte); break;
                case "sbyte": userType = typeof(sbyte); break;
                case "ushort": userType = typeof(ushort); break;
                case "short": userType = typeof(short); break;
                case "uint": userType = typeof(uint); break;
                case "int": userType = typeof(int); break;
                case "string": userType = typeof(string); break;
                case "decimal": userType = typeof(decimal); break;
                default:
                    userType = Type.GetType(typeName);
                    break;
            }

            Console.WriteLine("Selected type '" + userType.ToString() + "'");

            Console.WriteLine("Input Value:");
            string val = Console.ReadLine();

            object o = TypeDescriptor.GetConverter(userType).ConvertFrom(val);

            Console.WriteLine("<<<USING object>>>");
            LogObject(o);

            Console.WriteLine("<<<USING dynamic>>>");
            LogObject((dynamic)o);

            Console.WriteLine("<<<USING reflection>>>");
            Action<object> f = LogObject<object>;
            MethodInfo logger = f.Method.GetGenericMethodDefinition().MakeGenericMethod(userType);
            logger.Invoke(null, new[] { o });

            Console.WriteLine("<<<USING expression tree>>>");
            var p = new[] { Expression.Parameter(typeof(object)) };
            Expression<Action<object>> e =
                Expression.Lambda<Action<object>>(
                    Expression.Call(null,
                                    logger,
                                    Expression.Convert(p[0], userType)
                                   )
                , p);
            Action<object> a = e.Compile();
            a(o);
        }
    }
}

Yes, it can be done via expression trees. The advantage is that you get a delegate so repeated calls will be far faster than doing MethodInfo.Invoke() over and over again. The dynamic keyword can do this also.

Example:

What type would you like to use?
decimal
Selected type 'System.Decimal'
Input Value:
5.47
<<<USING object>>>
The object has static type 'System.Object',  dynamic type 'System.Decimal', and value '5.47'
<<<USING dynamic>>>
The object has static type 'System.Decimal',  dynamic type 'System.Decimal', and value '5.47'
<<<USING reflection>>>
The object has static type 'System.Decimal',  dynamic type 'System.Decimal', and value '5.47'
<<<USING expression tree>>>
The object has static type 'System.Decimal',  dynamic type 'System.Decimal', and value '5.47'

Code:

using System;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace SO2433436
{
    class Program
    {
        static void LogObject<T>(T t)
        {
            Console.WriteLine("The object has static type '" + typeof(T).FullName + "',  dynamic type '" + t.GetType() + "', and value '" + t.ToString() + "'");
        }

        static void Main(string[] args)
        {
            Console.WriteLine("What type would you like to use?");
            string typeName = Console.ReadLine();

            Type userType;
            switch (typeName)
            {
                case "byte": userType = typeof(byte); break;
                case "sbyte": userType = typeof(sbyte); break;
                case "ushort": userType = typeof(ushort); break;
                case "short": userType = typeof(short); break;
                case "uint": userType = typeof(uint); break;
                case "int": userType = typeof(int); break;
                case "string": userType = typeof(string); break;
                case "decimal": userType = typeof(decimal); break;
                default:
                    userType = Type.GetType(typeName);
                    break;
            }

            Console.WriteLine("Selected type '" + userType.ToString() + "'");

            Console.WriteLine("Input Value:");
            string val = Console.ReadLine();

            object o = TypeDescriptor.GetConverter(userType).ConvertFrom(val);

            Console.WriteLine("<<<USING object>>>");
            LogObject(o);

            Console.WriteLine("<<<USING dynamic>>>");
            LogObject((dynamic)o);

            Console.WriteLine("<<<USING reflection>>>");
            Action<object> f = LogObject<object>;
            MethodInfo logger = f.Method.GetGenericMethodDefinition().MakeGenericMethod(userType);
            logger.Invoke(null, new[] { o });

            Console.WriteLine("<<<USING expression tree>>>");
            var p = new[] { Expression.Parameter(typeof(object)) };
            Expression<Action<object>> e =
                Expression.Lambda<Action<object>>(
                    Expression.Call(null,
                                    logger,
                                    Expression.Convert(p[0], userType)
                                   )
                , p);
            Action<object> a = e.Compile();
            a(o);
        }
    }
}
冷情妓 2024-09-01 10:26:15

MethodInfo.MakeGenericMethod

然后创建委托并调用它。(当然不是在表达式中;p)

更新:

一般来说,我更喜欢为此使用泛型类型,Activator.CreateInstance 只需要更少的工作。不过,一切都取决于您的情况。

MethodInfo.MakeGenericMethod

Then just create a delegate and call it. (not in an expression, of course ;p)

Update:

Generally, I prefer to use generic types for this, Activator.CreateInstance just requires less work. All depends on your situation though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文