DynamicObject绑定WRT私有类型

时间:2011-08-22 08:50:46

标签: c# .net dynamic

请考虑以下事项:

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<T>课程更改为public而不是private,则可以按预期方式工作:

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

或者,相反,如果我将结果转换为IValueProvider<int>,它也有效:

// 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;
}

有谁可以指出我在这里缺少的东西?有没有办法让我能够使上面的代码工作而不需要使我的所有类型都公开并且没有强制转换?

2 个答案:

答案 0 :(得分:3)

有趣的是,如果可访问性为internal

,则有效
internal class ValueProvider<T> : IValueProvider<T> { ... }

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

现在可能会使用internal

当然,这里更好的方法是使用界面。你不需要 dynamic的IMO;您可能需要的是非通用的IValueProvider

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

with(在课堂上):

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

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

答案 1 :(得分:0)

一种解决方案是让ValueProvider<T>继承DynamicObject,然后在其上实施TryGetMember

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

当然在这个例子中,无论你试图获得什么属性,我们都只返回Value,但我可能会考虑使用字典或ExpandoObject来保持ValueProvider<T>上的可用属性