有什么方法可以使用 DynamicProxy 在 C# 中拦截对内部字典的访问?
Any way to intercept access to internal Dictionary in C# with DynamicProxy?
我有一个简单的 class 和拦截器设置(主要基于 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
的内部字典键时除外。有没有什么方法可以自动拦截该访问(我假设没有),或者我最好只限制对私有的访问并只允许通过函数访问?
执行 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()));
}
}
}
我有一个简单的 class 和拦截器设置(主要基于 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
的内部字典键时除外。有没有什么方法可以自动拦截该访问(我假设没有),或者我最好只限制对私有的访问并只允许通过函数访问?
执行 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()));
}
}
}