您的位置:首页 > 其它

Attribute鲜为人知的两个特性记录 推荐

2012-03-30 03:16 295 查看
Attribute作为一种标记在我们的.net中随处可见,比如DatContract,DatMember,Serializable等等,各种用途的标记。是的我们的代码更加简洁,对于Attribute用好了,可以很好的简化我们的开发,比如PostSharp的AOP实现就是一种基于Attribute的标记编译时注入。在随笔中有关于IOC,AOP利用Attribute标记简化开发的实例。

在使用Attribute时候发现了些鲜为人知的特性:

1:利用GetCustomAttributes传入的Attribute返回得到包括派生类。

2:GetCustomAttributes每次返回的对象都是经过发射出来的没有缓存。

1:GetCustomAttributes传入的Attribute返回得到包括派生类:

这里将采用一个测试类来验证:

[AttributeUsage(AttributeTargets.Class)]

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每次返回的对象都是经过发射出来的没有缓存:

测试代码可以看出来,不是同一个地址引用:

private static void TestAttributeActiver()

{

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):

internal static unsafe object[] GetCustomAttributes(Module decoratedModule, int decoratedMetadataToken, int pcaCount, RuntimeType attributeFilterType, bool mustBeInheritable, IList derivedAttributes)

{

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参数的检查是在我们的编译时期,参数必须是常量表达式,却在这里需要每次反射。

本篇随笔只是个人使用心得记录,请勿拍砖。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Attribute