【问题标题】:Ignore properties with custom attribute set, using reflection使用反射忽略具有自定义属性集的属性
【发布时间】:2016-11-14 15:44:23
【问题描述】:

我编写了一个泛型方法,它从数据表中生成泛型类型的集合。我已经寻找了不同的实现,但是在处理大量属性和大量记录时,它们中的大多数都表现得很糟糕。到目前为止,这个表现相当不错。

我尝试通过在属性顶部添加自定义属性 ( DataField ) 来改进该方法,这样我可以将其包含在属性中,我可以跳过将其与列匹配,或者为将匹配数据表的列名的属性。

我查看了代码,现在它看起来像一团糟,我真的不为它感到自豪,我希望有一个更好的实现。谁能给我一些提示?将不胜感激。

尝试将 cmets 包括在内,但不确定它有多大帮助。谢谢,代码如下:

private static void SetItemFromRow<T>(T item, DataRow row) where T : new()
    {
        // Get all properties with attributes.
        PropertyInfo[] propWithAttributes = item.GetType().GetProperties().Where(x => Attribute.IsDefined
          (x, typeof(DataField))).ToArray();

        foreach (DataColumn col in row.Table.Columns)
        {
            // Find property that matches the column name.
            PropertyInfo p = item.GetType().GetProperty(col.ColumnName);
            bool ignoreProperty = false;

            if (p != null)
            {
                // If no attribute exists set the property value. Break out from the loop to go to the next column (Property).
                if (!propWithAttributes.Contains(p))
                {
                    if (row[col] != DBNull.Value)
                    {
                        p.SetValue(item, row[col], null);
                        continue;
                    }
                }

                // If the property has a custom attribute then check if its ignore property is true. If so we break out from the loop and go to the next column (Property). 
                var attrs = p.GetCustomAttributes(typeof(DataField), false).ToArray() as DataField[]; ;

                if (attrs != null)
                    foreach (var attr in attrs)
                    {
                        if (attr.Ignore)
                            ignoreProperty = true;
                    }

                if (ignoreProperty) continue;
            }

            SetPropertyWithCustomName(item, propWithAttributes, row, col);    
        }
    }

现在我们已经在具有匹配列名称的对象上设置了所有属性,并且我们跳过了所有我们想要忽略的属性。最后一步是设置具有定义了 Name 的 DataField 属性的属性。

            private static void SetPropertyWithCustomName<T>(T item, PropertyInfo[] propWithAttributes,  DataRow row,  DataColumn col)
        where T : new()
    {

        foreach (var prop in propWithAttributes)
        {
            // Get the attributes for the property.
            var attrs = prop.GetCustomAttributes(typeof(DataField), false).ToArray() as DataField[];
            bool match = false;

            if (attrs != null)
            {
                foreach (var attr in attrs)
                {
                    // Check if the column name matches the custom name on the property.
                    if (col.ColumnName == attr.Name)
                    {
                        var p = item.GetType().GetProperty(prop.Name);
                        if (row[col] != DBNull.Value)
                        {
                            p.SetValue(item, row[col], null);
                            match = true;
                            break;
                        }
                    }
                }

            if (match) break;

            }
        }
    }

【问题讨论】:

  • 定义 nicier?更容易阅读?快点?内存效率更高?无论如何,代码审查已经完成there
  • 现在更可读了。
  • 谢谢,我会在“那里”试一试。
  • 您至少应该缓存您对 GetProperty 的所有调用。您为每个数据行对 GetProperty 进行了许多相同的调用。此外,使用属性后缀 (DataFieldAttribute) 命名属性也是一种常见约定。
  • 感谢您的提示。

标签: c# reflection custom-attributes


【解决方案1】:

这是您的代码更易读的版本(如果我理解正确的话):

private static readonly Dictionary<Type, DataFieldProperty[]> _propsCache = new Dictionary<Type, DataFieldProperty[]>();
private static DataFieldProperty[] GetProperties(Type type) {
    lock (_propsCache) {
        if (!_propsCache.ContainsKey(type)) {
            var result = new List<DataFieldProperty>();
            foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) {
                var attr = prop.GetCustomAttribute<DataField>();
                result.Add(new DataFieldProperty {
                    Name = attr?.Name ?? prop.Name,
                    Ignore = attr?.Ignore ?? false,
                    Property = prop
                });
           }
           _propsCache.Add(type, result.ToArray());
        }
        return _propsCache[type];
    }
}

private class DataFieldProperty {
    public string Name { get; set; }
    public PropertyInfo Property { get; set; }
    public bool Ignore { get; set; }
}

private static void SetItemFromRow<T>(T item, DataRow row) where T : new() {
    // Get all properties with attributes.
    var props = GetProperties(item.GetType());
    foreach (DataColumn col in row.Table.Columns) {
        // Find property that matches the column name.
        var p = props.FirstOrDefault(c => c.Name == col.ColumnName && !c.Ignore);
        if (p != null) {
            if (row[col] != DBNull.Value) {
                p.Property.SetValue(item, row[col], null);
            }
        }
    }
}

请注意,我实际上并没有运行它(但验证它可以编译)。

【讨论】:

  • 感谢您抽出宝贵时间,我会在一秒钟内测试并提供反馈。
  • 嗨,它的代码肯定少了很多,而且每个嵌套的代码也少了很多。我对其进行了测试并且运行良好,我没有 vs2015 和 C# 6,所以必须进行一些小调整,但它确实有效!谢谢!
  • 不客气。它还缓存 GetProperty 调用,因此在大型集合上应该更快一些。
  • 我唯一的问题是,为什么要在那里使用锁?
  • 因为您可以将它用于来自不同线程的不同行集。作为一般规则,对所有静态字段的访问应该是线程安全的。如果您确定 SetItemFromRow 不会被不同的线程同时调用,则可以移除锁定,但这很难强制执行。
猜你喜欢
  • 2017-05-31
  • 1970-01-01
  • 1970-01-01
  • 2012-03-13
  • 1970-01-01
  • 2011-03-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多