有没有办法拦截使用DynamicProxy在C#中访问内部Dictionary?

时间:2015-02-18 17:59:08

标签: c# castle-dynamicproxy

我设置了一个简单的类和拦截器(严重基于freezable示例),

using System;
using Castle.Core.Logging;
using System.Collections.Generic;
using System.Reflection;
using Castle.DynamicProxy;

[Serializable]
public class Pet
{
    public virtual string Name { get; set; }
    public virtual Dictionary<String, int> Dict { get; set; }

    public Pet()
    {
        Dict = new Dictionary<string, int>();
    }

    public override string ToString()
    {
        return string.Format("Name: {0}, Age: {1}", Name, Dict);
    }

    public object getFieldDirect(string name)
    {
        return GetType().BaseType.GetField(
                name,
                BindingFlags.Instance | BindingFlags.NonPublic
            ).GetValue(this);
    }
}

public class PetXample
{
    public PetXample()
    {
        Console.WriteLine( new NullLogger() );

        Pet rex;

        rex = Something.MakeSomething<Pet>();

        rex.Name = "Rex";
        rex.Dict["key"] = 2;
        rex.ToString();

        Console.WriteLine( rex.ToString() );
    }
}

public static class Something
{
    private static readonly ProxyGenerator _generator = new ProxyGenerator(new PersistentProxyBuilder());

    public static TSomething MakeSomething<TSomething>() where TSomething : class, new()
    {
        TSomething proxy = _generator.CreateClassProxy<TSomething>( new MyInterceptor() );
        return proxy;
    }
}

[Serializable]
public class MyInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        invocation.Proceed();

        if( invocation.Method.Name.StartsWith("set_") )
        {
            string shortName = invocation.Method.Name.Substring(4);
            string intName = "<" + shortName + ">k__BackingField";
            object obj = invocation.InvocationTarget;
            BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            FieldInfo fi = invocation.TargetType.GetField(
                intName,
                flags
            );
            Console.WriteLine( shortName + ": " + fi.FieldType + " - " + fi.GetValue(obj) );
        }
    }
}

除了访问Pet的内部词典键之外,其中我的工作方式完全正常。是否有任何方法可以自动拦截该访问权限(我假设没有),或者我最好只是限制访问私有权限而只允许通过功能访问?

1 个答案:

答案 0 :(得分:1)

执行rex.Dict["key"] = 2;

类似
    Dictionary<String, Int32> dico = rex.Dict;
    dico["key"] = 2;

如您所见,调用rex.Dict["key"] = 2;不会在rex实例上调用任何set方法。你的拦截器不会拦截任何东西。

要实现您想要的目标,您还必须代理字典。

例如:

    [Serializable]
    public class Pet
    {
        public virtual String Name { get; set; }
        public virtual IDictionary<String, Int32> Dict { get; set; }

        public Pet()
        {
            this.Dict = Something.MakeSomething<IDictionary<String, Int32>>(new Dictionary<String, Int32>());
        }

        public override String ToString()
        {
            return String.Format("Name: {0}, Age: {1}", Name, Dict);
        }
    }

    public static class Something
    {
        private static readonly ProxyGenerator _generator = new ProxyGenerator(new PersistentProxyBuilder());

        public static TSomething MakeSomething<TSomething>() where TSomething : class, new()
        {
            TSomething proxy = _generator.CreateClassProxy<TSomething>(new MyInterceptor());
            return proxy;
        }

        public static TSomething MakeSomething<TSomething>(TSomething instance) where TSomething : class
        {
            TSomething proxy = _generator.CreateInterfaceProxyWithTargetInterface<TSomething>(instance, new MyInterceptor());
            return proxy;
        }
    }

    [Serializable]
    public class MyInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            invocation.Proceed();

            if (invocation.Method.IsSpecialName && invocation.Method.Name.StartsWith("set_"))
            {
                PropertyInfo pi = invocation.TargetType.GetProperty(invocation.Method.Name.Substring(4), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

                Console.WriteLine("{0}[{1}]({2})", pi.Name, pi.PropertyType, String.Join(" - ", invocation.Arguments.Select(a => a.ToString()).ToArray()));
            }

        }
    }