attribute的使用场景
attribute不仅仅局限于c#中,在整个.net框架中都提供了非常大的拓展点,任何地方都有attribute的影子
- 编译器层
比如 obsolete,conditional - c#层
get,post,max,range,require - clr vm层
structlayout,dllimport - 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次造成的性能差距还是很明显的
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; }
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注入同步代码
其本质就是注入lock同步块代码,只是颗粒度在整个方法上。相对比较大
结论
attrubute在c#层面,底层使用反射。因此使用自定义attribute时,酌情使用缓存来提高性能
到此这篇关于.net core 特性(attribute)底层原理浅谈的文章就介绍到这了,更多相关.net core 底层原理内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论