处理对对象属性的每个获取或设置调用



我正在寻找一种在访问属性获取者和设置器时自动检查访问权限的好方法。

这只是出于好奇和实验目的,所以性能并不重要。

我发现的一种方法是以下方法(示例仅适用于getter):

public class DynamicPropertyClass : DynamicObject
{
    /// <summary>
    /// I want this to work for a default auto-property
    /// </summary>
    public string TestString { get; set; }
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (StaticSecurityContext.CheckSecurityCondition(binder.GetType(), binder.Name))
        {
            return base.TryGetMember(binder, out result);
        }
        else
        {
            throw new SecurityException();
        }
    }
}

如果未提供安全条件,则使用动态对象中止。此方法的问题在于,类上实际存在的属性不会由 TryGetMember 方法处理。因此,这种方法迫使我只处理代码中实际上不存在的属性。

如您所见,动态功能与我的方法并不真正相关。我想要一些类似的东西来处理用代码编写的属性,动态功能不必支持。

那么,在 c# 中是否有另一种方法,无需在每个属性上应用属性或向 getter 和 setter 添加自定义代码?

您可以使用代理模式。它存在AOP框架提供的很多实现,但如果性能不是条件,您可以简单地尝试远程处理API使用的透明/RealProxy。

https://msdn.microsoft.com/fr-fr/library/system.runtime.remoting.proxies.realproxy(v=vs.110).aspx

这是一个古老的问题,但我认为贡献会很有趣,因为没有提到 DynamicProxy。您也可以使用 Castle.Core 中的 DynamicProxy nuget 包。

可以截获对类的所有虚拟属性的 Get 和 Set 方法的调用。

我写了一个要点来解释这个概念,跟踪 访问和设置属性时。

这是拦截器的外观。

public class GetSetInterceptor : Interceptor
{
    protected override void ExecuteBefore(IInvocation invocation)
    {
    }
    protected override void ExecuteAfter(IInvocation invocation)
    {
        if(invocation.Method.Name.StartsWith("get_") || invocation.Method.Name.StartsWith("set_"))
        {
            var target = invocation.InvocationTarget as TrackedObject;
            if(target == null)
            {
                return;
            }
            var methodInvoked = invocation.Method.Name.Split("_");
            switch (methodInvoked[0])
            {
                case "get":
                    target.AddEvent(EventType.Get, methodInvoked[1], invocation.ReturnValue);
                    break;
                case "set":
                    target.AddEvent(EventType.Set, methodInvoked[1], invocation.Arguments[0]);
                    break;
            }
        }
    }
}

代理的创建是这样完成的:

    ProxyGenerator generator = new ProxyGenerator();
    var tracked = generator.CreateClassProxy<TrackedClass>(new GetSetInterceptor());

跟踪类必须具有虚拟属性:

public class TrackedClass : TrackedObject
{
    public virtual string SomeContent { get; set; }
    public virtual int SomeInt { get; set; }
}

测试在这里(使用 xUnit):

public class GetterSetterInterceptorTests
{
    [Fact]
    public void SuccessFullyRegisterGetAndSetEvents()
    {
        ProxyGenerator generator = new ProxyGenerator();
        var tracked = generator.CreateClassProxy<TrackedClass>(new GetSetInterceptor());
        tracked.SomeContent = "some content";
        Assert.Single(tracked.GetEvents());
        var eventAfterSomeContentAssigned = tracked.GetEvents().Last();
        Assert.Equal(EventType.Set, eventAfterSomeContentAssigned.EventType);
        Assert.Equal("some content", eventAfterSomeContentAssigned.Value);
        Assert.Equal("SomeContent", eventAfterSomeContentAssigned.PropertyInfo.Name);
        tracked.SomeInt = 1;
        Assert.Equal(2, tracked.GetEvents().Count);
        var eventAfterSomeIntAssigned = tracked.GetEvents().Last();
        Assert.Equal(EventType.Set, eventAfterSomeContentAssigned.EventType);
        Assert.Equal(1, eventAfterSomeIntAssigned.Value);
        Assert.Equal("SomeInt", eventAfterSomeIntAssigned.PropertyInfo.Name);
        var x = tracked.SomeInt;
        Assert.Equal(3, tracked.GetEvents().Count);
        var eventAfterSomeIntAccessed = tracked.GetEvents().Last();
        Assert.Equal(EventType.Get, eventAfterSomeIntAccessed.EventType);
        Assert.Equal(1, eventAfterSomeIntAccessed.Value);
        Assert.Equal("SomeInt", eventAfterSomeIntAccessed.PropertyInfo.Name);
    }
}

最新更新