DynamicObject 绑定 WRT 私有类型

发布于 2024-11-30 15:23:18 字数 2974 浏览 0 评论 0原文

考虑以下情况:

using System;
using System.Dynamic;

namespace DynamicTest
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic d = new DynamicTest();
            var v = d.foo();
            Console.WriteLine(v.Value);

            Console.ReadKey();
        }
    }

    public interface IValueProvider<T>
    {
        T Value
        {
            get;
        }
    }

    public class DynamicTest : DynamicObject
    {
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            result = new ValueProvider<int>(32);
            return true;
        }

        private class ValueProvider<T> : IValueProvider<T>
        {
            public ValueProvider(T t)
            {
                this.Value = t;
            }

            public T Value
            {
                get;
                private set;
            }
        }
    }
}

这在运行时失败:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled
  Message='object' does not contain a definition for 'Value'
  Source=Anonymously Hosted DynamicMethods Assembly
  StackTrace:
       at CallSite.Target(Closure , CallSite , Object )
       at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
       at DynamicTest.Program.Main(String[] args)
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

如果我将 ValueProvider 类更改为 public 而不是 private ,它会按预期工作:

// this allows it to work
public class ValueProvider<T> : IValueProvider<T>

或者,相反,如果我将结果转换为 IValueProvider 它也可以工作:

// this also allows it to work
dynamic d = new DynamicTest();
var v = (IValueProvider<int>)d.foo();

如果我尝试使用该接口作为 TryInvokeMember 中的编译时类型,它不会工作(没想到,但我想我会尝试):

// this doesn't help
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
    var valueProvider = (IValueProvider<int>)new ValueProvider<int>(32);
    result = valueProvider;
    return true;
}

任何人都可以指出我在这里缺少什么吗?有没有办法让我在不公开所有类型且不进行强制转换的情况下使上面的代码正常工作?

Consider the following:

using System;
using System.Dynamic;

namespace DynamicTest
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic d = new DynamicTest();
            var v = d.foo();
            Console.WriteLine(v.Value);

            Console.ReadKey();
        }
    }

    public interface IValueProvider<T>
    {
        T Value
        {
            get;
        }
    }

    public class DynamicTest : DynamicObject
    {
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            result = new ValueProvider<int>(32);
            return true;
        }

        private class ValueProvider<T> : IValueProvider<T>
        {
            public ValueProvider(T t)
            {
                this.Value = t;
            }

            public T Value
            {
                get;
                private set;
            }
        }
    }
}

This fails at runtime with:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled
  Message='object' does not contain a definition for 'Value'
  Source=Anonymously Hosted DynamicMethods Assembly
  StackTrace:
       at CallSite.Target(Closure , CallSite , Object )
       at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
       at DynamicTest.Program.Main(String[] args)
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

If I change the ValueProvider<T> class to public rather than private it works as expected:

// this allows it to work
public class ValueProvider<T> : IValueProvider<T>

Or, instead, if I cast the result to IValueProvider<int> it also works:

// this also allows it to work
dynamic d = new DynamicTest();
var v = (IValueProvider<int>)d.foo();

If I try using the interface as the compile-time type in TryInvokeMember, it doesn't work (didn't really expect it to, but thought I'd try):

// this doesn't help
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
    var valueProvider = (IValueProvider<int>)new ValueProvider<int>(32);
    result = valueProvider;
    return true;
}

Can anyone point out what I'm missing here? Is there a way for me to get the code above working without resorting to making all my types public and without casting?

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

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

发布评论

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

评论(2

余生再见 2024-12-07 15:23:18

有趣的是,如果可访问性是内部的,它就可以工作:

internal class ValueProvider<T> : IValueProvider<T> { ... }

正如 Hassan 所说(评论),这是有道理的,因为这是 Program 使用 API 通常需要的可访问性DynamicTest 的成员(在同一程序集中)。如果我们将 Main 移至 DynamicTest 并将其保留为 private,它会再次起作用,这是我们应该期望的正确可访问性。

也许现在使用internal

当然,更好的方法是仅使用接口。 IMO 你在这里不需要需要动态;您可能需要的是一个非通用的 IValueProvider:

public interface IValueProvider
{
    object Value { get; }
}
public interface IValueProvider<T> : IValueProvider
{
    new T Value{get; }
}

with (in the class):

object IValueProvider.Value { get { return Value; } }

and:

var v = (IValueProvider)d.foo();
Console.WriteLine(v.Value);

Interestingly, it works if the accessibility is internal:

internal class ValueProvider<T> : IValueProvider<T> { ... }

which as Hassan notes (comments) makes sense, as that is the accessibility that would normally be required for Program to use the API members of DynamicTest (in the same assembly). If we move Main to DynamicTest and leave it private it again works, which is the correct accessibility we should expect.

Maybe use internal for now.

Of course, an even better approach here would be to just use the interface. IMO you don't need dynamic here; what you perhaps need is a non-generic IValueProvider:

public interface IValueProvider
{
    object Value { get; }
}
public interface IValueProvider<T> : IValueProvider
{
    new T Value{get; }
}

with (in the class):

object IValueProvider.Value { get { return Value; } }

and:

var v = (IValueProvider)d.foo();
Console.WriteLine(v.Value);
小镇女孩 2024-12-07 15:23:18

一种解决方案是让 ValueProvider 也继承 DynamicObject,然后在其上实现 TryGetMember

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = this.Value;
            return true;
        }

当然,在本例中我们是无论您尝试获取什么属性,都只是返回值,但我可能会考虑使用字典或 ExpandoObject 来保留 ValueProvider 上的可用属性

One solution would be to make ValueProvider<T> inherit DynamicObject aswell, and then implementing TryGetMember on that:

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = this.Value;
            return true;
        }

of course in this example we're just returning the Value no matter what property you try to get, but I'd probably look into using a dictionary or an ExpandoObject to keep the available properties on ValueProvider<T>

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