zoukankan      html  css  js  c++  java
  • Github 开源:高效好用的对象间属性拷贝工具:升讯威 Mapper( Sheng.Mapper)

    Github 地址https://github.com/iccb1013/Sheng.Mapper

     

    对象属性值映射/拷贝工具。不需要创建映射规则,不要求对象类型一致,适用于简单直接的拷贝操作,可以全属性拷贝,指定属性拷贝,排除指定的属性。拷贝包含 10 个属性的对象 10 万次,耗时 4.x 秒(普通开发机)。

     

     

    + 拷贝行为只针对 sourceObject 和 targetObject 所共有的属性
    + 在 sourceObject 和 targetObject 中的待拷贝的属性值的类型处理:如果是值类型,直接拷贝,如果是引用类型,sourceObject 中的属性的类型 必须 和 targetObject 中的属性的类型一致,或是它的派生类
    + 如果要支持类型不一致的属性自动进行类型转换,你可以在 PropertyMappingDescription 这个类中实现转换器功能
    + 拷贝行为 不会 改变 targetObject 中不需要被拷贝的属性的值
    + 你可以组合使用几个方法来从多个对象中拷贝指定的属性值到一个 targetObject

    和 AutoMapper 互补,与之相比最大优势是短,平,快。不需要创建复杂的映射规则,并支持属性排除操作

     

    具体实现:

    这里在具体实现上,其实并不复杂,只需对反射操作稍有了解即可,

    我们通过 sourceObject 和 targetObject ,获取它们的“类型(Type)”,然后使用 Type.GetProperties() 方法,获取这个对象类型所包含的属性(Property)。

     PropertyInfo[] propertyList = Type.GetProperties();
                foreach (PropertyInfo property in propertyList)
                {
                    PropertyMappingDescription propertyMappingDescription = new PropertyMappingDescription(property);
    
                    _propertyList.Add(propertyMappingDescription);
                    _propertyNames.Add(property.Name, propertyMappingDescription);
                }

    这里有另外一个细节需要留意的是,我们要把同样类型(Type)的相关信息,缓存起来,这样下次再拷贝相同类型的对象时,就无需再去反射它的 Properties。

    我们通过 TypeMappingDescription 对对象的类型信息进行缓存和包装,提供我们所需要的基本操作:

    public bool ContainsProperty(string name)
            {
                if (String.IsNullOrEmpty(name))
                    throw new ArgumentNullException("TypeMappingDescription.ContainsProperty 必须指定属性名。");
    
                return _propertyNames.ContainsKey(name);
            }       
    
            public object GetValue(object obj, string propertyName)
            {
                if (obj == null)
                    throw new ArgumentNullException("指定的对象为空。");
    
                if (obj.GetType() != this.Type)
                    throw new ArgumentException("指定的对象类型与缓存的对象类型不一致。");
    
                if (_propertyNames.ContainsKey(propertyName) == false)
                    throw new ArgumentOutOfRangeException("指定的属性名不存在。");
    
                PropertyMappingDescription propertyMappingDescription = (PropertyMappingDescription)_propertyNames[propertyName];
                if (propertyMappingDescription.CanRead == false)
                    throw new InvalidOperationException("属性 " + propertyName + "不可读。");
    
                return propertyMappingDescription.GetValue(obj);
            }
    
            public void SetValue(object obj, string propertyName, object value)
            {
                if (obj == null)
                    throw new ArgumentNullException("指定的对象为空。");
    
                if (obj.GetType() != this.Type)
                    throw new ArgumentException("指定的对象类型与缓存的对象类型不一致。");
    
                if (_propertyNames.ContainsKey(propertyName) == false)
                    throw new ArgumentOutOfRangeException("指定的属性名不存在。");
    
                PropertyMappingDescription propertyMappingDescription = (PropertyMappingDescription)_propertyNames[propertyName];
                if (propertyMappingDescription.CanWrite == false)
                    throw new InvalidOperationException("属性 " + propertyName + "只读。");
    
                Type propertyType = propertyMappingDescription.PropertyInfo.PropertyType;
                if (propertyType.IsValueType == false && value != null)
                {
                    Type valueType = value.GetType();
                    if(propertyType != valueType && valueType.IsSubclassOf(propertyType) == false)
                    {
                        throw new ArgumentException("目标对象的 " + propertyName + "与 value 的类型既不一致,也不是目标类型的派生类。");
                    }
                }
    
                propertyMappingDescription.SetValue(obj, value);
            }

    同时我们使用 PropertyMappingDescription 对 PropertyInfo 进行封装。对 PropertyInfo 进行封装,是为了方便我们后续针对属性添加属性值的转换器,以便实现稍复杂一些的属性拷贝操作。

    最后我们来测试一下拷贝操作:

     A a = new A()
                {
                    Name = "张三",
                    Age = 10,
                    Class = "一班",
                    CObject = new SubC()
                    {
                        Message = "Hello"
                    },
                    P1 = "1",
                    P2 = "2",
                    P3 = "3",
                    P4 = "4",
                    P5 = "5",
                    P6 = "6"
                };
    
                B b = new B();
    
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
    
                for (int i = 0; i < 100000; i++)
                {
                    //全部属性拷贝
                    ShengMapper.SetValues(a, b);
    
                    //拷贝指定的属性
                   // ShengMapper.SetValuesWithProperties(a, b, new string[] { "Name", "Age", "P1" });
    
                    //排除指定的属性
                    //ShengMapper.SetValuesWithoutProperties(a, b, new string[] { "Name", "Age", "P1" });
    
                    
                }
    
                stopwatch.Stop();
    
                Console.WriteLine("对包含 10 个属性的对象的属性值拷贝 10 万次,耗时:" + stopwatch.Elapsed.ToString());
    
                Console.ReadLine();

    我模拟了一几个类,他们有不同类型的属性,还包括引用类型的属性我派生类。

    对于包含 10 个属性的类的 10 万次属性值拷贝,在开发机上大约用了 4.x 秒。

    完整的代码位于 Github。

    https://github.com/iccb1013/Sheng.Mapper

  • 相关阅读:
    ASP.NET Web Optimization Framework
    HearthBuddy Plugin编写遇到的问题
    HearthBuddy的plugin加载
    Unexpected ConvertTo-Json results? Answer: it has a default -Depth of 2
    HearthBuddy卡牌无法识别
    HearthstoneBot
    网络传输中的三张表,MAC地址表、ARP缓存表以及路由表
    DNS原理及其解析过程(转)
    React系列之--props属性
    react中constructor( )和super( )的具体含义以及如何使用
  • 原文地址:https://www.cnblogs.com/sheng_chao/p/7026732.html
Copyright © 2011-2022 走看看