C#自定义Attribute值的获取与优化技巧

 更新时间:2023年07月05日 11:54:48   作者:junjieok  
C#自定义Attribute值的获取是开发中会经常用到的,大家通常使用反射进行获取的,代码也很简单,今天通过本文给大家讲解C# Attribute值获取方法,感兴趣的朋友跟随小编一起看看吧

首先说下什么是Atrribute

首先,我们肯定Attribute是一个类,下面是msdn文档对它的描述:

公共语言运行时允许你添加类似关键字的描述声明,叫做attributes,它对程序中的元素进行标注,如类型、字段、方法和属性等。Attributes和Microsoft .NET Framework文件的元数据保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。

在.NET中,Attribute被用来处理多种问题,比如序列化、程序的安全特征、防止即时编译器对程序代码进行优化从而代码容易调试等等。下面,我们先来看几个在.NET中标准的属性的使用,稍后我们再回过头来讨论Attribute这个类本身。(文中的代码使用C#编写,但同样适用所有基于.NET的所有语言)

Attribute作为编译器的指令

在C#中存在着一定数量的编译器指令,如:#define DEBUG, #undefine DEBUG, #if等。这些指令专属于C#,而且在数量上是固定的。而Attribute用作编译器指令则不受数量限制。比如下面的三个Attribute:

   Conditional:起条件编译的作用,只有满足条件,才允许编译器对它的代码进行编译。一般在程序调试的时候使用。

   DllImport:用来标记非.NET的函数,表明该方法在一个外部的DLL中定义。

   Obsolete:这个属性用来标记当前的方法已经被废弃,不再使用了。

C# Attribute值获取

C#自定义Attribute值的获取是开发中会经常用到的,一般我们的做法也就是用反射进行获取的,代码也不是很复杂。

1、首先有如下自定义的Attribute

[AttributeUsage(AttributeTargets.All)]
    public sealed class NameAttribute : Attribute
    {
        private readonly string _name;
        public string Name
        {
            get { return _name; }
        }
        public NameAttribute(string name)
        {
            _name = name;
        }
    }

2、定义一个使用NameAttribute的类

[Description("Customer Information")]
[Name("customer_info")]
public class CustomerInfo
{
    [Name("name")]
    public string Name { get; set; }
    [Name("address")]
    public string Address;
}

3、获取CustomAttributes类上的"dept"也就很简单了

private static string GetName()
        {
            var type = typeof(CustomAttributes);
            var attribute = type.GetCustomAttributes(typeof(NameAttribute), false).FirstOrDefault();
            if (attribute == null)
            {
                return null;
            }
            return ((NameAttribute)attribute).Name;
        }

以上代码就可以简单的获取,类上的Attribute的值了,但是需求往往不是这么简单的,不仅要获取类头部Attribute上的值,还要获取字段Address头部Attribute上的值。有的同学可能就觉得这还不简单呀,直接上代码

private static string GetAddress()
        {
            var type = typeof (CustomAttributes);
            var fieldInfo = type.GetField("Address");
            if (fieldInfo == null)
            {
                return null;
            }
            var attribute = fieldInfo.GetCustomAttributes(typeof(NameAttribute), false).FirstOrDefault();
            if (attribute == null)
            {
                return null;
            }
            return ((NameAttribute) attribute).Name;
        }

上面代码就是获取Address字段头部上的Attribute值了。虽然我们是获取到了我们想要的,但是我们发现这样做是不是太累了,如果又扩展一个自定义的Attribute,或者又在一个新的属性或字段上标上Attribute时,我们又要写一段代码来实现我想要的,这些严重代码违反了DRY的设计原则。我们知道获取Attribute是通过反射来取的,Attribute那个值又是不变的,这样就没必要每次都要进行反射来获取了。基于以上两点代码进行了如下的优化,优化后的代码如下:

using System;
using System.Collections.Concurrent;
using System.Reflection;
public static class CustomAttributeExtensions
{
    /// <summary>
    /// Cache Data
    /// </summary>
    private static readonly ConcurrentDictionary<string, object> Cache = new ConcurrentDictionary<string, object>();
    /// <summary>
    /// 获取CustomAttribute Value
    /// </summary>
    /// <typeparam name="TAttribute">Attribute的子类型</typeparam>
    /// <typeparam name="TReturn">TReturn的子类型</typeparam>
    /// <param name="sourceType">头部标有CustomAttribute类的类型</param>
    /// <param name="attributeValueAction">取Attribute具体哪个属性值的匿名函数</param>
    /// <returns>返回Attribute的值,没有则返回null</returns>
    public static TReturn GetCustomAttributeValue<TAttribute, TReturn>(this Type sourceType, Func<TAttribute, TReturn> attributeValueAction)
        where TAttribute : Attribute
    {
        return _getAttributeValue(sourceType, attributeValueAction, null);
    }
    /// <summary>
    /// 获取CustomAttribute Value
    /// </summary>
    /// <typeparam name="TAttribute">Attribute的子类型</typeparam>
    /// <typeparam name="TReturn">TReturn的子类型</typeparam>
    /// <param name="sourceType">头部标有CustomAttribute类的类型</param>
    /// <param name="attributeValueAction">取Attribute具体哪个属性值的匿名函数</param>
    /// <param name="propertyName">field name或property name</param>
    /// <returns>返回Attribute的值,没有则返回null</returns>
    public static TReturn GetCustomAttributeValue<TAttribute, TReturn>(this Type sourceType, Func<TAttribute, TReturn> attributeValueAction, string propertyName)
        where TAttribute : Attribute
    {
        return _getAttributeValue(sourceType, attributeValueAction, propertyName);
    }
    #region private methods
    private static TReturn _getAttributeValue<TAttribute, TReturn>(Type sourceType, Func<TAttribute, TReturn> attributeFunc, string propertyName)
        where TAttribute : Attribute
    {
        var cacheKey = BuildKey<TAttribute>(sourceType, propertyName);
        var value = Cache.GetOrAdd(cacheKey, k => GetValue(sourceType, attributeFunc, propertyName));
        if (value is TReturn) return (TReturn)Cache[cacheKey];
        return default(TReturn);
    }
    private static string BuildKey<TAttribute>(Type type, string propertyName) where TAttribute : Attribute
    {
        var attributeName = typeof(TAttribute).FullName;
        if (string.IsNullOrEmpty(propertyName))
        {
            return type.FullName + "." + attributeName;
        }
        return type.FullName + "." + propertyName + "." + attributeName;
    }
    private static TReturn GetValue<TAttribute, TReturn>(this Type type, Func<TAttribute, TReturn> attributeValueAction, string name)
        where TAttribute : Attribute
    {
        TAttribute attribute = default(TAttribute);
        if (string.IsNullOrEmpty(name))
        {
            attribute = type.GetCustomAttribute<TAttribute>(false);
        }
        else
        {
            var propertyInfo = type.GetProperty(name);
            if (propertyInfo != null)
            {
                attribute = propertyInfo.GetCustomAttribute<TAttribute>(false);
            }
            else
            {
                var fieldInfo = type.GetField(name);
                if (fieldInfo != null)
                {
                    attribute = fieldInfo.GetCustomAttribute<TAttribute>(false);
                }
            }
        }
        return attribute == null ? default(TReturn) : attributeValueAction(attribute);
    }
    #endregion
}

优化后的代码:

把不同的代码用泛型T,Fun<TAttribute,TReturn>来处理来减少重复的代码;
把取过的Attribute值存到一个ConcurrentDictionary中,下次再来取时,如果有则直接取ConcurrentDictionary中的值,如果没有才通过反射来取相应的Attribute值,这样大大的提高效率;  

调用方法也更加的简单了,代码如下:

var customerInfoName = typeof(CustomerInfo).GetCustomAttributeValue<NameAttribute, string>(x => x.Name);
var customerAddressName = typeof(CustomerInfo).GetCustomAttributeValue<NameAttribute, string>(x => x.Name, "Address");
var customerInfoDesc = typeof(CustomerInfo).GetCustomAttributeValue<DescriptionAttribute, string>(x => x.Description);
Console.WriteLine("CustomerInfo Name:" + customerInfoName);
Console.WriteLine("customerInfo >Address Name:" + customerAddressName);
Console.WriteLine("customerInfo Desc:" + customerInfoDesc);

运行结果:

到此这篇关于C#自定义Attribute值的获取与优化的文章就介绍到这了,更多相关C# Attribute值获取内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C# WinForm实现图片浏览器

    C# WinForm实现图片浏览器

    这篇文章主要为大家详细介绍了C# WinForm实现图片浏览器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • C#实现多种图片格式转换的示例详解

    C#实现多种图片格式转换的示例详解

    这篇文章主要为大家详细介绍了C#如何实现多种图片格式转换,例如转换成图标图像ICO,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-01-01
  • c#多线程中Lock()关键字的用法小结

    c#多线程中Lock()关键字的用法小结

    本篇文章主要是对c#多线程中Lock()关键字的用法进行了详细的总结介绍,需要的朋友可以过来参考下,希望对大家有所帮助
    2014-01-01
  • C#利用iTextSharp添加PDF水印

    C#利用iTextSharp添加PDF水印

    这篇文章主要为大家详细介绍了C#利用iTextSharp添加PDF水印的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-03-03
  • C#处理TCP数据的方法详解

    C#处理TCP数据的方法详解

    Tcp是一个面向连接的流数据传输协议,用人话说就是传输是一个已经建立好连接的管道,数据都在管道里像流水一样流淌到对端,那么数据必然存在几个问题,比如数据如何持续的读取,数据包的边界等,本文给大家介绍了C#处理TCP数据的方法,需要的朋友可以参考下
    2024-06-06
  • DevExpress实现GridControl根据列选中一行

    DevExpress实现GridControl根据列选中一行

    这篇文章主要介绍了DevExpress实现GridControl根据列选中一行,比较实用的功能,需要的朋友可以参考下
    2014-08-08
  • C#备忘录模式(Memento Pattern)实例教程

    C#备忘录模式(Memento Pattern)实例教程

    这篇文章主要介绍了C#备忘录模式(Memento Pattern),以一个支持回退操作的例子讲述了C#备忘模式的实现方法,需要的朋友可以参考下
    2014-09-09
  • 基于C#实现屏幕取色器的示例详解

    基于C#实现屏幕取色器的示例详解

    这篇文章主要为大家详细介绍了如何利用C#实现屏幕取色器,文中的示例代码讲解详细,对我们学习C#有一定的帮助,感兴趣的小伙伴可以了解一下
    2022-12-12
  • C#中dynamic的使用方法及应用场景

    C#中dynamic的使用方法及应用场景

    在 C# 编程中,dynamic 类型是一个非常特殊的类型,它在编译时并不会进行类型检查,而是在运行时才进行类型解析,本文将详细讲解 dynamic 的使用方法、优缺点以及一些实际应用场景,需要的朋友可以参考下
    2024-08-08
  • c# 快速排序算法

    c# 快速排序算法

    快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)
    2013-10-10

最新评论