Attribute鲜为人知的两个特性记录
2012-03-28 00:33 破狼 阅读(5041) 评论(2) 编辑 收藏 举报Attribute作为一种标记在我们的.net中随处可见,比如DatContract,DatMember,Serializable等等,各种用途的标记。是的我们的代码更加简洁,对于Attribute用好了,可以很好的简化我们的开发,比如PostSharp的AOP实现就是一种基于Attribute的标记编译时注入。在随笔中有关于IOC,AOP利用Attribute标记简化开发的实例。
在使用Attribute时候发现了些鲜为人知的特性:
1:利用GetCustomAttributes传入的Attribute返回得到包括派生类。
2:GetCustomAttributes每次返回的对象都是经过发射出来的没有缓存。
1:GetCustomAttributes传入的Attribute返回得到包括派生类:
这里将采用一个测试类来验证:
public class TestImplementsAttribute : Attribute
{
public string Name
{ get; set; }
}
private static void TestMutilpeImplements()
{
var type = typeof(Program);
var attrs = type.GetCustomAttributes(typeof(TestImplementsAttribute), false);
Console.WriteLine(string.Format("TestImplementsAttribute:({0})",attrs.Length));
foreach (var item in attrs)
{
Console.WriteLine(" " + item.GetType().FullName);
}
attrs = type.GetCustomAttributes(typeof(SerializableAttribute), false);
Console.WriteLine(string.Format("SerializableAttribute:({0})", attrs.Length));
foreach (var item in attrs)
{
Console.WriteLine(" " + item.GetType().FullName);
}
attrs = type.GetCustomAttributes(typeof(Attribute), false);
Console.WriteLine(string.Format("(base type)Attribute:({0})", attrs.Length));
foreach (var item in attrs)
{
Console.WriteLine(" " + item.GetType().FullName);
}
}
输出为:
这里我们可以很清晰的看见当传入Attribute类型时候返回包含了SerializableAttribute和TestImplementsAttribute两个。
2:GetCustomAttributes每次返回的对象都是经过发射出来的没有缓存:
测试代码可以看出来,不是同一个地址引用:
{
var type = typeof(Program);
var attr1 = type.GetCustomAttributes(typeof(TestImplementsAttribute), false)[0];
var attr2 = type.GetCustomAttributes(typeof(TestImplementsAttribute), false)[0];
Console.WriteLine(Object.ReferenceEquals(attr1, attr2));
}
输出值为false。
我们在看看
.下面是 reflector的反编译结果(Attribute.GetCustomAttributes):
{
if (decoratedModule.Assembly.ReflectionOnly)
{
throw new InvalidOperationException(Environment.GetResourceString("Arg_ReflectionOnlyCA"));
}
MetadataImport metadataImport = decoratedModule.MetadataImport;
CustomAttributeRecord[] customAttributeRecords = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken);
Type elementType = (((attributeFilterType == null) || attributeFilterType.IsValueType) || attributeFilterType.ContainsGenericParameters) ? typeof(object) : attributeFilterType;
if ((attributeFilterType == null) && (customAttributeRecords.Length == 0))
{
return (Array.CreateInstance(elementType, 0) as object[]);
}
object[] attributes = Array.CreateInstance(elementType, customAttributeRecords.Length) as object[];
int length = 0;
SecurityContextFrame frame = new SecurityContextFrame();
frame.Push(decoratedModule.Assembly.InternalAssembly);
Assembly lastAptcaOkAssembly = null;
for (int i = 0; i < customAttributeRecords.Length; i++)
{
bool flag2;
bool flag3;
object obj2 = null;
CustomAttributeRecord caRecord = customAttributeRecords[i];
RuntimeMethodHandle ctor = new RuntimeMethodHandle();
RuntimeType attributeType = null;
int namedArgs = 0;
IntPtr signature = caRecord.blob.Signature;
IntPtr blobEnd = (IntPtr) (((void*) signature) + caRecord.blob.Length);
if (FilterCustomAttributeRecord(caRecord, metadataImport, ref lastAptcaOkAssembly, decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable, attributes, derivedAttributes, out attributeType, out ctor, out flag2, out flag3))
{
if (!ctor.IsNullHandle())
{
ctor.CheckLinktimeDemands(decoratedModule, decoratedMetadataToken);
}
RuntimeConstructorInfo.CheckCanCreateInstance(attributeType, flag3);
if (flag2)
{
obj2 = CreateCaObject(decoratedModule, ctor, ref signature, blobEnd, out namedArgs);
}
else
{
obj2 = attributeType.TypeHandle.CreateCaInstance(ctor);
if (Marshal.ReadInt16(signature) != 1)
{
throw new CustomAttributeFormatException();
}
signature = (IntPtr) (((void*) signature) + 2);
namedArgs = Marshal.ReadInt16(signature);
signature = (IntPtr) (((void*) signature) + 2);
}
for (int j = 0; j < namedArgs; j++)
{
string str;
bool flag4;
Type type3;
object obj3;
IntPtr ptr1 = caRecord.blob.Signature;
GetPropertyOrFieldData(decoratedModule, ref signature, blobEnd, out str, out flag4, out type3, out obj3);
try
{
if (flag4)
{
if ((type3 == null) && (obj3 != null))
{
type3 = (obj3.GetType() == typeof(RuntimeType)) ? typeof(Type) : obj3.GetType();
}
RuntimePropertyInfo property = null;
if (type3 == null)
{
property = attributeType.GetProperty(str) as RuntimePropertyInfo;
}
else
{
property = attributeType.GetProperty(str, type3, Type.EmptyTypes) as RuntimePropertyInfo;
}
RuntimeMethodInfo setMethod = property.GetSetMethod(true) as RuntimeMethodInfo;
if (setMethod.IsPublic)
{
setMethod.MethodHandle.CheckLinktimeDemands(decoratedModule, decoratedMetadataToken);
setMethod.Invoke(obj2, BindingFlags.Default, null, new object[] { obj3 }, null, true);
}
}
else
{
(attributeType.GetField(str) as RtFieldInfo).InternalSetValue(obj2, obj3, BindingFlags.Default, Type.DefaultBinder, null, false);
}
}
catch (Exception exception)
{
throw new CustomAttributeFormatException(string.Format(CultureInfo.CurrentUICulture, Environment.GetResourceString(flag4 ? "RFLCT.InvalidPropFail" : "RFLCT.InvalidFieldFail"), new object[] { str }), exception);
}
}
if (!signature.Equals(blobEnd))
{
throw new CustomAttributeFormatException();
}
attributes[length++] = obj2;
}
}
frame.Pop();
if ((length == customAttributeRecords.Length) && (pcaCount == 0))
{
return attributes;
}
if (length == 0)
{
Array.CreateInstance(elementType, 0);
}
object[] destinationArray = Array.CreateInstance(elementType, (int) (length + pcaCount)) as object[];
Array.Copy(attributes, 0, destinationArray, 0, length);
return destinationArray;
}
在这里我们可以见数组的创建CreateInstance等等。
同时可以参见老赵前辈以前的关于Attribute反射的一次失败的尝试(上):原来GetCustomAttributes方法每次都返回新的实例和一次失败的尝试(下):无法使用泛型的Attribute。
不知道为什么在Attribute参数的检查是在我们的编译时期,参数必须是常量表达式,却在这里需要每次反射。
本篇随笔只是个人使用心得记录,请勿拍砖。
作者:破 狼
出处:http://www.cnblogs.com/whitewolf/
本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。该文章也同时发布在我的独立博客中-个人独立博客、博客园--破狼和51CTO--破狼。