每当第一次使用对象时自动调用 init 函数

Automatically calling an init function whenever an object is used for the 1st time

我有一个对象,它只在构造时(快速)用准系统数据初始化自己,并在第一次访问时真正(慢)加载自己。这个想法是我在启动时创建了很多这些准系统对象并将它们散列到一个映射中,然后在第一次单独访问每个对象时完全加载它。问题是我无法保证客户端将如何与该对象交互,可能会调用多个 public 方法。

有没有好的模式支持这种情况?显而易见的(也是我当前的)解决方案是使用内部 bool 跟踪状态,检查每个可能调用的函数中的 bool,然后以这种方式加载。但这需要在所有 public 函数中复制该行为的代码,并且容易出错。

我可以想象一个单一的入口点方法,然后根据客户端请求类型等发出行为,但在我考虑走这条路之前,我想看看是否有一个普遍接受的 approach/pattern 我可能不知道。我在 C# 中执行此操作,但欢迎任何见解。

如果我理解你想要实现的目标,那么你正在寻找代理设计模式,更具体地说,是一个虚拟代理。

参考http://www.dofactory.com/net/proxy-design-pattern

一个小例子是这样的:

    public abstract class IObjectProvider
    {
        public abstract IObjectProvider Object{get;}
        public abstract void doStuff();
    }

    public class RealObject : IObjectProvider
    {
        public RealObject()
        {
            //Do very complicated and time taking stuff;
        }
        public override IObjectProvider Object
        {
            get { return this; }
        }

        public override void doStuff()
        {
            //do this stuff that these objects normally do 
        }
    }

    public class ObjectProxy : IObjectProvider
    {
        private IObjectProvider objectInstance = null;
        public override IObjectProvider Object
        {
            get 
            {
                if (objectInstance == null)
                    objectInstance = new RealObject();
                return objectInstance; 
            }
        }

        public override void doStuff()
        {
            if(objectInstance!=null)
                objectInstance.doStuff();
        }
    }

    public class SkeletonClass 
    {
        public IObjectProvider Proxy1 = new ObjectProxy();
        public IObjectProvider Proxy2 = new ObjectProxy();
    }
    static void Main(String[] args)
    {
        //Objects Not Loaded
        SkeletonClass skeleton = new SkeletonClass();

        //Proxy1 loads object1 on demand
        skeleton.Proxy1.Object.doStuff();

        //Proxy2 not loaded object2 until someone needs it
    }

下面是动态代理方法的示例。

using System;
using System.Diagnostics;
using Castle.DynamicProxy;  //Remember to include a reference, too.  It's nugettable package is Castle.Core

namespace ConsoleApp
{
    public class ActualClass
    {
        //Have static instances of two below for performance
        private static ProxyGenerator pg = new ProxyGenerator();
        private static ActualClassInterceptor interceptor = new ActualClassInterceptor();

        //This is how we get ActualClass items that are wrapped in the Dynamic Proxy
        public static ActualClass getActualClassInstance()
        {
            ActualClass instance = new ActualClass();
            return pg.CreateClassProxyWithTarget<ActualClass>(instance, interceptor);
        }

        //Tracking whether init has been called
        private bool initialized = false;

        //Will be used as evidence of true initialization, i.e. no longer null
        private int? someValue = null;

        public void Initialize()
        {
            if (!initialized)
            {
                //do some initialization here.
                someValue = -1; //Will only get set to non-null if we've run this line.
                initialized = true;
            }
        }

        //Any methods you want to intercept need to be virtual!
        public virtual int replaceValue(int value) 
        {
            //below will blow up, if someValue has not been set to -1 via Initialize();
            int oldValue = someValue.Value;
            someValue = value;
            return oldValue;
        }

        //block off constructor from public to enforce use of getActualClassInstance
        protected ActualClass() { }
    }

    public class ActualClassInterceptor : ActualClass, IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            //Call initialize before proceeding to call the intercepted method
            //Worth noting that this is the only place we actually call Initialize()
            ((ActualClass)invocation.InvocationTarget).Initialize();
            invocation.Proceed();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ActualClass instance1 = ActualClass.getActualClassInstance();
            ActualClass instance2 = ActualClass.getActualClassInstance();
            int x1 = instance1.replaceValue(41);
            int x2 = instance2.replaceValue(42);

            int y1 = instance1.replaceValue(82);
            Debug.Assert(y1 == 41);

            int y2 = instance2.replaceValue(84);
            Debug.Assert(y2 == 42);

            var read = Console.ReadKey();
        }
    }
}