本文模拟实现的不是精细到某个属性的延迟加载,仅 item级别的延迟加载。

在如今的数据编程,或多或少都用过 orm 来进行数据库数据的操作,特别是 微软的 ef 也是深受欢迎。

而 ef 中有个特性,即延迟加载。就是当你查询一个列表出来,在tolist之前或者是遍历之前,它的item数据还是不全面的,只有当你遍历到某个对象时,它才会去查出这个对象各个属性的值,这样有一个好处就是,单次查询的数据量不会很大,很多东西我们不需要就不用查询出来,避免浪费空间占用。

不过本次模拟实现的效果并不是很细化到某个属性的动态加载,仅仅是模拟对象延迟创建。

要进行可控的延迟创建,自然是需要我们自己实现 IEnumerator

而这个 IEnumerator 实现需要有一下几点需要注意:

1.原列表数据只有 id列表

2.需要提供对象创建委托,在需要的时候动态创建对象

3.创建的对象缓存起来,第二次用到时将不再进行创建

一下直接放代码:

首先是自定义实现的 列表(包含迭代器)

 

/// <summary>
    /// 仅模拟实现,实际情况复杂的多
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class MyList<T> : IEnumerable<T>
    {
        /// <summary>
        /// 数据的唯一标识
        /// </summary>
        private string[] ids;
        /// <summary>
        /// 获取详细信息的构造器,该构造器根据 id 获取到item的信息
        /// </summary>
        private Func<string, object> itemCreator;
        public MyList(Func<string, object> itemCreator,params string[] ids)
        {
            this.ids = ids;
            this.itemCreator = itemCreator;
            
        }
        public IEnumerator<T> GetEnumerator()
        {
            return new MyListEnumeratorT<T>(GetMyEnumerator());
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetMyEnumerator();
        }

        private IEnumerator GetMyEnumerator()
        {
            return new MyListEnumerator(ids, itemCreator); ;
        }
    }
    /// <summary>
    /// 这里相当使用装饰器扩展而已,无其它实现
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class MyListEnumeratorT<T> : IEnumerator<T>
    {
        private IEnumerator myListEnumerator;
        public MyListEnumeratorT(IEnumerator myListEnumerator)
        {
            this.myListEnumerator = myListEnumerator;
        }
        public T Current => (T)myListEnumerator.Current;

        object IEnumerator.Current => myListEnumerator;

        public void Dispose()
        {
            ;
        }

        public bool MoveNext()
        {
            return myListEnumerator.MoveNext();
        }

        public void Reset()
        {
            myListEnumerator.Reset();
        }
    }

    public class MyListEnumerator : IEnumerator
    {
        /// <summary>
        /// id列表
        /// </summary>
        private string[] ids;
        /// <summary>
        /// 对象构造器
        /// </summary>
        private Func<string, object> itemCreator;
        /// <summary>
        /// 对象缓存
        /// </summary>
        private Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
        /// <summary>
        /// 索引
        /// </summary>
        private int index = 0;
        public MyListEnumerator(string[] ids,Func<string,object>itemCreator)
        {
            this.ids = ids;
            this.itemCreator = itemCreator;
        }
        public object Current { get; set; }

        public bool MoveNext()
        {
            if(ids.Length > index)
            {
                
                if(!keyValuePairs.ContainsKey(ids[index]))
                {
                    Debug.WriteLine("未加载的对象这个时候进行加载");
                    keyValuePairs.Add(ids[index], itemCreator.Invoke(ids[index]));
                }
                Current = keyValuePairs[ids[index]];
                index++;
                return true;
            }
            return false;
        }

        public void Reset()
        {
            index = 0;
        }
    }

测试代码:

public class SetUp
    {
        [STAThread]
        public static void Main()
        {
            /*
             * 模拟测试 延迟加载(item层面)
             * 假设 sql查询只根据条件查询出 id (列表),之查出一个字段而且是唯一索引字段,这个效率会快很多,数据量也不大
             * 在枚举各个对象时,再根据具体的id获取详细信息(延迟获取详细信息,单词传输的数据量会变少,但也导致请求次数也会相应的增多,看到通过动态代理在获取item某个属性的值才去请求数据,那个就更细但请求次数也更多)
             */

            var ids = "one,two,three,four,five".Split(',');

            {
                var myList = new MyList<MyModel>(ItemCreator, ids);
                Console.WriteLine("会在用到的时候才创建(每个创建时间间隔 0.5s+)");
                foreach (MyModel item in myList)
                {
                    Thread.Sleep(500);
                    Console.WriteLine(string.Format("{0}:{1}", item.CreateTime, item.Id));
                }
            }

            {
                //直接toList 会创建所有?
                Console.WriteLine("直接toList 会马上创建所有(输出的创建时间只有几毫秒)");
                var myList2 = new MyList<MyModel>(ItemCreator, ids).ToList();

                foreach (MyModel item in myList2)
                {
                    Thread.Sleep(500);
                    Console.WriteLine(string.Format("{0}:{1}", item.CreateTime, item.Id));
                }
            }

            {
                //获取某个对象时会生成该对象
                Console.WriteLine("获取某个对象时会生成该对象");
                var item = new MyList<MyModel>(ItemCreator, ids).FirstOrDefault();
                Console.WriteLine(string.Format("{0}:{1}", item.CreateTime, item.Id));
            }


            Console.ReadLine();
        }
        /// <summary>
        /// 对象创建器
        /// 在实际使用过程中,可以是一个根据 id查询详细信息 这样的一个方法
        /// 而且实际查询也不一定是查询所有字段的值,所以orm做延迟加载处理的东西不少()
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        private static object ItemCreator(string id)
        {
            Console.WriteLine(string.Format("创建id:{0}",id));
            return new MyModel
            {
                Id = id,
                CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")
            };
        }


        class MyModel
        {
            public string Id { get; set; }
            public string CreateTime { get; set; }
        }
    }

执行效果:

c#模拟列表对象延迟加载

相关文章:

  • 2022-12-23
  • 2021-07-20
  • 2022-12-23
  • 2021-12-04
  • 2021-08-21
猜你喜欢
  • 2021-11-03
  • 2021-12-05
  • 2021-06-17
  • 2021-11-22
  • 2021-12-15
  • 2021-11-17
相关资源
相似解决方案