调用动态对象的成员,其名称在运行时在字符串中定义

发布于 2024-11-17 11:06:44 字数 843 浏览 2 评论 0原文

我想在利用 DLR 绑定机制的同时访问对象的属性。

  • 我无法使用本机绑定机制(C# 中的dynamic 关键字),因为我在编译时不知道属性名称;
  • 我不能使用反射,因为它只检索静态类型信息;
  • 据我所知,转换为 IDictionary 只能解决选择实现该接口的动态类(例如 ExpandoObject)的情况。

这是演示代码:

    static void Main(string[] args)
    {
        dynamic obj = new System.Dynamic.ExpandoObject();
        obj.Prop = "Value";
        // C# dynamic binding.
        Console.Out.WriteLine(obj.Prop);
        // IDictionary<string, object>
        Console.Out.WriteLine((obj as IDictionary<string, object>)["Prop"]);
        // Attempt to use reflection.
        PropertyInfo prop = obj.GetType().GetProperty("Prop");
        Console.Out.WriteLine(prop.GetValue(obj, new object[] { }));
        Console.In.ReadLine();
    }

I want to access a property on an object while leveraging the DLR binding mechanism.

  • I cannot use the native binding mechanism (dynamic keyword in C#) because I do not know the property name at compile-time;
  • I cannot use reflection because it only retrieves static type information;
  • casting to an IDictionary<string, object>, to my knowledge, only solves the case of dynamic classes that choose to implement that interface (such as ExpandoObject).

Here is the demonstration code:

    static void Main(string[] args)
    {
        dynamic obj = new System.Dynamic.ExpandoObject();
        obj.Prop = "Value";
        // C# dynamic binding.
        Console.Out.WriteLine(obj.Prop);
        // IDictionary<string, object>
        Console.Out.WriteLine((obj as IDictionary<string, object>)["Prop"]);
        // Attempt to use reflection.
        PropertyInfo prop = obj.GetType().GetProperty("Prop");
        Console.Out.WriteLine(prop.GetValue(obj, new object[] { }));
        Console.In.ReadLine();
    }

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

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

发布评论

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

评论(5

不一样的天空 2024-11-24 11:06:44

我终于能够使用 C# 运行时绑定程序来完成此操作。 (我相信应该可以使用更简单的绑定器实现来做到这一点,但我无法编写一个有效的实现 - 可能编写一个“简单”实现已经相当困难)。

using Microsoft.CSharp.RuntimeBinder;

    private class TestClass
    {
        public String Prop { get; set; }
    }

    private static Func<object, object> BuildDynamicGetter(Type targetType, String propertyName)
    {
        var rootParam = Expression.Parameter(typeof(object));
        var propBinder = Microsoft.CSharp.RuntimeBinder.Binder.GetMember(CSharpBinderFlags.None, propertyName, targetType, new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
        DynamicExpression propGetExpression = Expression.Dynamic(propBinder, typeof(object), 
            Expression.Convert(rootParam, targetType));
        Expression<Func<object, object>> getPropExpression = Expression.Lambda<Func<object, object>>(propGetExpression, rootParam);
        return getPropExpression.Compile();
    }

    static void Main(string[] args)
    {
        dynamic root = new TestClass { Prop = "StatValue" };

        Console.Out.WriteLine(root.Prop);
        var dynGetter = BuildDynamicGetter(root.GetType(), "Prop");
        Console.Out.WriteLine(dynGetter(root));

        root = new System.Dynamic.ExpandoObject();
        root.Prop = "ExpandoValue";

        Console.WriteLine(BuildDynamicGetter(root.GetType(), "Prop").Invoke(root));
        Console.Out.WriteLine(root.Prop);
        Console.In.ReadLine();
    }

输出:

StatValue
StatValue
ExpandoValue
ExpandoValue

I was finally able to do it by using the C# runtime binder. (I believe it should be possible to do that with a simpler binder implementation, but I was unable to write a working one - probably writing a "simple" implementation is already quite tough).

using Microsoft.CSharp.RuntimeBinder;

    private class TestClass
    {
        public String Prop { get; set; }
    }

    private static Func<object, object> BuildDynamicGetter(Type targetType, String propertyName)
    {
        var rootParam = Expression.Parameter(typeof(object));
        var propBinder = Microsoft.CSharp.RuntimeBinder.Binder.GetMember(CSharpBinderFlags.None, propertyName, targetType, new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
        DynamicExpression propGetExpression = Expression.Dynamic(propBinder, typeof(object), 
            Expression.Convert(rootParam, targetType));
        Expression<Func<object, object>> getPropExpression = Expression.Lambda<Func<object, object>>(propGetExpression, rootParam);
        return getPropExpression.Compile();
    }

    static void Main(string[] args)
    {
        dynamic root = new TestClass { Prop = "StatValue" };

        Console.Out.WriteLine(root.Prop);
        var dynGetter = BuildDynamicGetter(root.GetType(), "Prop");
        Console.Out.WriteLine(dynGetter(root));

        root = new System.Dynamic.ExpandoObject();
        root.Prop = "ExpandoValue";

        Console.WriteLine(BuildDynamicGetter(root.GetType(), "Prop").Invoke(root));
        Console.Out.WriteLine(root.Prop);
        Console.In.ReadLine();
    }

Output:

StatValue
StatValue
ExpandoValue
ExpandoValue
旧瑾黎汐 2024-11-24 11:06:44

开源框架 dynamitey 将执行此操作(可通过 nuget)。它使用与 C# 编译器相同的 api 调用。

Console.Out.WriteLine(Dynamic.InvokeGet(obj,"Prop"));

The opensource framework dynamitey will do this (available via nuget). It uses that same api calls as the c# compiler.

Console.Out.WriteLine(Dynamic.InvokeGet(obj,"Prop"));
紫南 2024-11-24 11:06:44

您可以将 C# dynamic 与运行时编译结合使用,以满足未知属性名称的要求。

一个例子:

dynamic d = new ExpandoObject();

d.Value = 7;

var helper = "" +
    "using System; " +
    "public class Evaluator " + 
    "{{ " + 
    "    public object Eval(dynamic d) {{ return d.{0}; }} " + 
    "}}";

var references = new string[] 
{ 
    "System.dll", 
    "System.Core.dll", 
    "Microsoft.CSharp.dll" 
};

var parameters = new CompilerParameters(references, "Test.dll");
var compiler = new CSharpCodeProvider();

var results = compiler.CompileAssemblyFromSource(
    parameters,
    String.Format(helper, "Value"));

dynamic exp = Activator.CreateInstance(
    results.CompiledAssembly.GetType("Evaluator"));

Console.WriteLine(exp.Eval(d));

这可行,但我怀疑这是最好的选择,如果您需要调用方法,它可能会变得更复杂一些。

You could use C# dynamic with runtime compilation to accommodate the requirement of the unknown property name.

An example:

dynamic d = new ExpandoObject();

d.Value = 7;

var helper = "" +
    "using System; " +
    "public class Evaluator " + 
    "{{ " + 
    "    public object Eval(dynamic d) {{ return d.{0}; }} " + 
    "}}";

var references = new string[] 
{ 
    "System.dll", 
    "System.Core.dll", 
    "Microsoft.CSharp.dll" 
};

var parameters = new CompilerParameters(references, "Test.dll");
var compiler = new CSharpCodeProvider();

var results = compiler.CompileAssemblyFromSource(
    parameters,
    String.Format(helper, "Value"));

dynamic exp = Activator.CreateInstance(
    results.CompiledAssembly.GetType("Evaluator"));

Console.WriteLine(exp.Eval(d));

This works, but I doubt that this is the best option and if you need invoke method it can get a bit more complex.

香草可樂 2024-11-24 11:06:44

如果您子类化DynamicObject,则可以覆盖TrySetMember 按名称将属性存储在本地字典中,并覆盖 TryGetIndex 以使用属性名称作为字符串来检索它们。 DynamicObject 页面上有一个很好的示例在 MSDN 上。

这不是最快的方法,因为它没有充分利用动态调度和调用站点(以及其他我不太理解的东西),但它会起作用。它最终会看起来像这样:

dynamic myDynamicClass = new MyDynamic();

// Uses your TrySetMember() override to add "Value" to the internal dictionary against the key "Prop":
myDynamicClass.Prop = "Value";

// Uses your TryGetIndex override to return the value of the internal dictionary entry for "Prop":
object value = myDynamicClass["Prop"];

If you subclass DynamicObject you can override TrySetMember to store the properties by name in a local dictionary, and override TryGetIndex to enable retrieving them using the property name as a string. There's a pretty good example of this at the DynamicObject page on MSDN.

This is not the fastest way to do it as it doesn't take advantage of dynamic dispatch and callsites (and other stuff I don't quite understand) as well as it could, but it will work. It'll end up looking like this:

dynamic myDynamicClass = new MyDynamic();

// Uses your TrySetMember() override to add "Value" to the internal dictionary against the key "Prop":
myDynamicClass.Prop = "Value";

// Uses your TryGetIndex override to return the value of the internal dictionary entry for "Prop":
object value = myDynamicClass["Prop"];
晨曦慕雪 2024-11-24 11:06:44

这基本上是这个问题的一个变体:动态添加成员到动态对象,但您不想添加,而是想获取成员 - 因此只需更改活页夹和调用站点的签名即可。看来您已经弄清楚如何制作活页夹了。

This is basically a variant of this question: Dynamically adding members to a dynamic object but instead of wanting to add you're wanting to get the members - so just change the binder and the signature of the call site. It looks like you've already figured out how to make the binder.

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