自动实现装饰器方法

时间:2018-04-03 13:44:39

标签: c# decorator

我想不出一个更好的方法来表达这一点所以它可能就在那里,但我不知道它的用语。我有许多用于访问不同数据存储的类,它们遵循以下模式:

interface IUserData {
  User GetUser(uint id);
  User ByName(string username);
}

class UserData : IUserData {
  ...
}

class AuthorizedUserData : IUserData {
  IUserData _Data = new UserData();

  public User GetUser(uint id) {
    AuthorizationHelper.Instance.Authorize();
    return _Data.GetUser(id);
  }

  public User ByName(string name) {
    AuthorizationHelper.Instance.Authorize();
    return _Data.ByName(name);
  }
}

所以基本设置是:

  1. 创建界面
  2. 创建实际实现该接口的具体类
  3. 为该类创建一个包装器,在调用具体类
  4. 之前执行相同的工作

    鉴于这些类实现了相同的接口,并且在包装类的每个方法的开头完成了完全相同的工作,这使我认为我可以自动化这个包装过程。

    我知道在JavaScript和Python中可以创建这样的装饰器。

    JavaScript中的示例:

    function AuthorizedUserData() {
      ...
    }
    
    const userDataPrototype = Object.getPrototypeOf(new UserData());
    Object.getOwnPropertyNames(userDataPrototype)
      .forEach(name => {
        const val = userDataPrototype[name];
        if (typeof val !== 'function') {
          return;
        }
    
        AuthorizedUserData.prototype[name] = function(...args) {
          AuthorizationHelper.Authorize();
          return this._Data[name](...args);
        };
      });
    

    这种自动实现是否可以在C#中使用?

2 个答案:

答案 0 :(得分:1)

使用任何依赖注入(DI)框架。以下使用WindsorCastle

使用nuget安装Windsor Castle。

拦截器可以在您的场景中用于拦截对方法的任何请求。

可以通过实现IInterceptor

来创建拦截器
public class AuthorizedUserData : IInterceptor
{
   public void Intercept(IInvocation invocation)
   {
       //Implement validation here
   }
}

使用DI容器注册依赖项并注册拦截器和类

var container = new WindsorContainer();
container.Register(Castle.MicroKernel.Registration.Component.For<AuthorizedUserData>().LifestyleSingleton());
container.Register(
               Castle.MicroKernel.Registration
               .Classes
               .FromAssemblyInThisApplication()
               .BasedOn<IUserData>()
               .WithServiceAllInterfaces().Configure(
                   x => x.Interceptors<AuthorizedUserData>()));

您的类和接口结构如下所示

    public interface IUserData
    {
        User GetUser(uint id);
        User ByName(string username);
    }

    public class UserData : IUserData
    {
        public User GetUser(uint id)
        {
            throw new System.NotImplementedException();
        }

        public User ByName(string username)
        {
            throw new System.NotImplementedException();
        }
    }


    public class User
    {

    }

然后使用DI容器来解析您需要的实例。在这里,我们需要一个IUserData

的实例
var user = container.Resolve<IUserData>(); // Creates an instance of UserData
user.ByName("userName"); //This call will first goto `Intercept` method and you can do validation.

答案 1 :(得分:0)

您正在寻找与 AOP 相关的内容。在.Net中,您可以使用RealProxy这个抽象类。

这是一个很简单的方法。

创建一个类并继承RealProxypublic override IMessage Invoke(IMessage msg)是您的编织点。

internal class DynamicProxy<T> : RealProxy
    where T : MarshalByRefObject,new()
{
    private T _target;

    private IMethodCallMessage callMethod = null;

    private IMethodReturnMessage returnMethod = null;

    public DynamicProxy(T target) : base(typeof(T))
    {
        _target = target;
    }

    public override IMessage Invoke(IMessage msg)
    {
        callMethod = msg as IMethodCallMessage;
        MethodInfo targetMethod = callMethod.MethodBase as MethodInfo;

        FilterInfo Attrs = new FilterInfo(_target, targetMethod);


        ExcuteingContext excuting = Excuting(Attrs.ExcuteFilters);
        if (excuting.Result != null)
        {
            returnMethod = GetReturnMessage(excuting.Result, excuting.Args);
        }
        else
        {
            InvokeMethod(targetMethod, excuting);

            ExcutedContext excuted = Excuted(Attrs.ExcuteFilters);
        }

        return returnMethod;
    }

    private void InvokeMethod(MethodInfo targetMethod, ExcuteingContext excuting)
    {
        object result = targetMethod.Invoke(_target, excuting.Args);
        returnMethod = GetReturnMessage(result, excuting.Args);
    }


    private ExcutedContext Excuted(IList<IExcuteFilter> filters)
    {
        ExcutedContext excutedContext = new ExcutedContext(returnMethod);

        foreach (var filter in filters)
        {
            filter.OnExcuted(excutedContext);
            if (excutedContext.Result != null)
                break;
        }

        return excutedContext;
    }

    private ExcuteingContext Excuting(IList<IExcuteFilter> filters)
    {
        ExcuteingContext excuteContext = new ExcuteingContext(callMethod);

        foreach (var filter in filters)
        {
            filter.OnExcuting(excuteContext);
            if (excuteContext.Result != null)
                break;
        }

        return excuteContext;
    }

    private ReturnMessage GetReturnMessage(object result, object[] args)
    {
        return new ReturnMessage(result,
                                    args,
                                    args.Length,
                                    callMethod.LogicalCallContext,
                                    callMethod);
    }
}    

在代理类

上获取AopBaseAttribute过滤器(编织)
/// <summary>
/// Getting filter Attribute on proxy class
/// </summary>
public class FilterInfo
{
    private List<IExcuteFilter> _excuteFilters = new List<IExcuteFilter>();

    public FilterInfo(MarshalByRefObject target, MethodInfo method)
    {
        //search for class Attribute
        var classAttr = target.GetType().GetCustomAttributes(typeof(AopBaseAttribute), true);
        //search for method Attribute
        var methodAttr = Attribute.GetCustomAttributes(method, typeof(AopBaseAttribute), true);

        var unionAttr = classAttr.Union(methodAttr);

        _excuteFilters.AddRange(unionAttr.OfType<IExcuteFilter>());
    }

    public IList<IExcuteFilter> ExcuteFilters
    {
        get
        {
            return _excuteFilters;
        }
    }
}

创建界面 IExcuteFilterFilterInfo获取并编织

public interface IExcuteFilter
{
    void OnExcuted(ExcutedContext excuteContext);

    void OnExcuting(ExcuteingContext excutingContext);
}

封装用于执行方法的上下文。

public class ExcuteingContext 
{
    public ExcuteingContext(IMethodCallMessage callMessage)
    {
        Args = callMessage.Args;
        MethodName = callMessage.MethodName;
    }

    public object[] Args { get; set; }

    public string MethodName { get; set; }

    public object Result { get; set; }
}

为Excuted方法封装上下文。

public class ExcutedContext
{
    public ExcutedContext(IMethodReturnMessage returnMethod)
    {
        Args = returnMethod.Args;
        MethodName = returnMethod.MethodName;
        Result = returnMethod.ReturnValue;
    }

    public object[] Args { get; set; }

    public string MethodName { get; set; }

    public object Result { get; set; }
}

/// <summary>
/// Filter AttributeBase
/// </summary>
public abstract class AopBaseAttribute : Attribute, IExcuteFilter
{

    public virtual void OnExcuted(ExcutedContext context)
    {
    }

    public virtual void OnExcuting(ExcuteingContext context)
    {
    }
}

/// <summary>
/// Customer Filter
/// </summary>
public class AuthorizedUserDataAttribute : AopBaseAttribute
{
    public override void OnExcuting(ExcuteingContext context)
    {
        //Console.WriteLine("Test");
        //Implement validation here
    }
}

ProxyFactory提供代理实例。

public class ProxyFactory
{
    public static TInterface GetInstance<TInterface,TObj>() 
        where TObj : MarshalByRefObject,new()
    {
        TObj proxyObj = Activator.CreateInstance(typeof(TObj)) as TObj;
        return (TInterface)new DynamicProxy<TObj>(proxyObj).GetTransparentProxy();
    }
}

如果您想使用需要拨打ProxyFactory.GetInstance<IUserData, UserData>

UserData必须继承MarshalByRefObject才能成为透明代理。

public interface IUserData
{
    User GetUser(uint id);
    User ByName(string username);
}

public class User
{
    public string name { get; set; }
}

[AuthorizedUserData]
public class UserData : MarshalByRefObject,IUserData
{
    public User GetUser(uint id)
    {
        return new User() { };
    }
    public User ByName(string username)
    {
        return new User() { };
    }
}

IUserData user = ProxyFactory.GetInstance<IUserData, UserData>();
user.ByName("1");

RealProxy

相关问题