.NET Core 特性(Attribute)底层原理浅谈

简介

.NET Core 特性(Attribute)底层原理浅谈
烂大街的资料不再赘述,简单来说就是给代码看的注释

Attribute的使用场景

Attribute不仅仅局限于C#中,在整个.NET框架中都提供了非常大的拓展点,任何地方都有Attribute的影子

  1. 编译器层
    比如 Obsolete,Conditional
  2. C#层
    GET,POST,Max,Range,Require
  3. CLR VM层
    StructLayout,DllImport
  4. JIT 层
    MethodImpl

Attribute在C#中的调用

举个常用的例子,读取枚举上的自定义特性。

    public enum Test     {         [EnumDescription("hhhhhh")]         None = 0,         [EnumDescription("xxxxxx")]         Done =1     } 	private static IEnumerable<string> GetEnumDescriptions(this Enum e) 	{ 		IEnumerable<string> result = null;         var type = e.GetType();         var fieldInfo = type.GetField(e.ToString());         var attr = fieldInfo?.GetCustomAttributes(typeof(EnumDescriptionAttribute), false);         if (attr?.Length > 0)         { 			result = attr.Cast<EnumDescriptionAttribute>().Select(x => x.Description);         } 		return result ?? Enumerable.Empty<string>(); 	} 

可以看到,Attribute底层在C#中实现依旧是依赖反射,所以为什么说Attribute是写给代码看的注释,因此对反射的优化思路也可以用在Attribute中。
比如在代码中,使用Dictionary缓存结果集。避免过多调用反射造成的性能问题。

        private static IEnumerable<string> GetEnumDescriptionsCache(this Enum e)         {             var key = $"{e.GetType().Name}_{e.ToString()}";             if (_enumMap.ContainsKey(key))             {                 return _enumMap[key];             }             else             {                 var result = GetEnumDescriptions(e);                 _enumMap.TryAdd(key, result);                 return result;             }         } 

循环100000次造成的性能差距还是很明显的
.NET Core 特性(Attribute)底层原理浅谈

Newtonsoft.Json对Attrubute的使用

以JsonConverter为蓝本举例说明。

    public class Person     {         [JsonConverter(typeof(DateTimeConverter))]         public DateTime CreateTime { get; set; }     } 	public class DateTimeConverter : JsonConverter<DateTime>     {         public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)         {             if (reader.Value == null)                 return DateTime.MinValue;              if (DateTime.TryParse(reader.Value.ToString(), out DateTime result))                 return result;              return DateTime.MinValue;         }          public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)         {             writer.WriteValue(value.ToString("yyyy-MM-dd HH:mm:ss"));         }     } 

定义了一个Attribute:JsonConverter.其底层调用如下:

        [RequiresUnreferencedCode(MiscellaneousUtils.TrimWarning)]         [RequiresDynamicCode(MiscellaneousUtils.AotWarning)]         public static JsonConverter? GetJsonConverter(object attributeProvider)         { 			// 底层还是调用Reflection,为了性能,也缓存了对象元数据。             JsonConverterAttribute? converterAttribute = GetCachedAttribute<JsonConverterAttribute>(attributeProvider);              if (converterAttribute != null)             {                 Func<object[]?, object> creator = CreatorCache.Instance.Get(converterAttribute.ConverterType);                 if (creator != null)                 {                     return (JsonConverter)creator(converterAttribute.ConverterParameters);                 }             }              return null;         } 

https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs

Attribute在CLR上的调用

    public class NativeMethods     {         [DllImport("xxxxx", EntryPoint = "add", CallingConvention = CallingConvention.Cdecl)]         public extern static int ManagedAdd(int a, int b);     } 

在CLR中,同样用来调用 C/C++ 的导出函数。有兴趣的朋友可以使用windbg查看线程调用栈。以及在MetaData中有一张ImplMap表,存储着C#方法与C++函数的mapping关系

Attribute在JIT上的调用

    public class Person     {         public int id { get; set; } = 0;          [MethodImpl(MethodImplOptions.Synchronized)]         public void SyncMethod()         {             id++;         }     } 

JIT会自动为该Attribute注入同步代码
.NET Core 特性(Attribute)底层原理浅谈
.NET Core 特性(Attribute)底层原理浅谈

其本质就是注入lock同步块代码,只是颗粒度在整个方法上。相对比较大

结论

Attrubute在C#层面,底层使用反射。因此使用自定义Attribute时,酌情使用缓存来提高性能

发表评论

相关文章