具有自定义属性和GetCustomAttributes的奇怪行为
本文关键字:GetCustomAttributes 自定义属性 | 更新日期: 2023-09-27 18:28:45
我已经解决这个问题好几个小时了,在SO(或谷歌)上找不到任何相关的东西。
这是我的问题:我有一个包含对象数组属性的自定义属性。
[System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false)]
public class Property : System.Attribute
{
public object[] Parameters { get; set; }
public JsonProperty(object[] prms = null)
{
Parameters = prms;
}
}
然后我使用以下代码从属性中读取它:
var customProperties = (Property[])currentProperty.GetCustomAttributes(typeof(Property), false);
这一切都适用于以下情况:
[Property(Parameters = new object[]{}]
<...property...>
然而,如果我将其设置为空([Property(Parameters = null])
,我会得到以下错误:
System.Reflection.CustomAttributeFormatException:
'Parameters' property specified was not found.
这很荒谬,因为属性是在我的自定义属性中定义的。我真的不明白。
所以我的问题是:到底发生了什么?
--编辑
如果我将属性的类型从object[]更改为object,那么指定null就可以了。
--编辑以添加代码
属性:
[System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false)]
public class JsonProperty : System.Attribute
{
public object[] Parameters { get; set; }
public JsonProperty(object[] prms = null)
{
Parameters = prms;
}
}
类别:
public class MyClass
{
[JsonProperty(Parameters = null)]
public DateTime Start { get; set; }
}
方法:
public string getAttributes()
{
Type t = MyClass.GetType();
// Get only public properties and that have been set.
var properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(prop => prop.GetValue(this, null) != null);
foreach (var prop in properties)
{
//The error occur on the next line.
var jsonProperties =
(JsonProperty[])prop.GetCustomAttributes(typeof(JsonProperty), false);
--如果你不明白,试着读一下:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/ddebbec6-1653-4502-9802-0b421efec60d/an-unexplicable-customattributeformatexception-from-getcustomattributes?forum=csharpgeneral
我也在那里问过这个问题。
我知道以前的帖子,但有一个变通办法。我在使用反射和自定义属性时也遇到了类似的问题。我更改了属性以更新设置值,如果设置值为Null,如下所示。
public object[] Parameters { get; set; }
已更改为:
private object[] _Parameters = new object[0];
public object[] Parameters {
get {
return _Parameters;
}
set {
_Parameters = value ?? new object[0];
}
}
因此,现在,即使您指定Null或未能指定值,它也会起作用,但如果您希望跳过设置为Null的属性,则可能需要在其他地方更新您的逻辑。
在我的例子中,这也是在处理int[]而不是object[]的数组时发生的。
我使用System.Runtime.Serialization.OptionalFieldAttribute
时也出现了同样的错误。要复制它,只需添加一个字段,用属性标记它,然后调用FieldInfo.GetCustomAttributes()
就足够了。
[OptionalField(VersionAdded = 0)]
private int _testA = 0;
private void SomeMethod()
{
FieldInfo fieldInfo = GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(p => p.Name == "_testA").FirstOrDefault();
fieldInfo.GetCustomAttributes();
//You will get a CustomAttributeFormatException('OptionalField' property specified was not found) for VersionAdded values less then 1.
}
好的问题是为什么!正如我所看到的,我们可以从GetCustomAttributes()
的代码中得到答案,其中有一个try{}catch{}
,它捕获所有错误并抛出CustomAttributeFormatException
,所以我认为我的错误来自OptionalFieldAttribute
:的setter
[AttributeUsage(AttributeTargets.Field, Inherited=false)]
[System.Runtime.InteropServices.ComVisible(true)]
public sealed class OptionalFieldAttribute : Attribute
{
int versionAdded = 1;
public OptionalFieldAttribute() { }
public int VersionAdded
{
get {
return this.versionAdded;
}
set {
if (value < 1)
throw new ArgumentException(Environment.GetResourceString("Serialization_OptionalFieldVersionValue"));
Contract.EndContractBlock();
this.versionAdded = value;
}
}
}
[System.Security.SecurityCritical]
private unsafe static object[] GetCustomAttributes(
RuntimeModule decoratedModule, int decoratedMetadataToken, int pcaCount,
RuntimeType attributeFilterType, bool mustBeInheritable, IList derivedAttributes, bool isDecoratedTargetSecurityTransparent)
{
if (decoratedModule.Assembly.ReflectionOnly)
throw new InvalidOperationException(Environment.GetResourceString("Arg_ReflectionOnlyCA"));
Contract.EndContractBlock();
MetadataImport scope = decoratedModule.MetadataImport;
CustomAttributeRecord[] car = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken);
bool useObjectArray = (attributeFilterType == null || attributeFilterType.IsValueType || attributeFilterType.ContainsGenericParameters);
Type arrayType = useObjectArray ? typeof(object) : attributeFilterType;
if (attributeFilterType == null && car.Length == 0)
return CreateAttributeArrayHelper(arrayType, 0);
object[] attributes = CreateAttributeArrayHelper(arrayType, car.Length);
int cAttributes = 0;
// Custom attribute security checks are done with respect to the assembly *decorated* with the
// custom attribute as opposed to the *caller of GetCustomAttributes*.
// Since this assembly might not be on the stack and the attribute ctor or property setters we're about to invoke may
// make security demands, we push a frame on the stack as a proxy for the decorated assembly (this frame will be picked
// up an interpreted by the security stackwalker).
// Once we push the frame it will be automatically popped in the event of an exception, so no need to use CERs or the
// like.
SecurityContextFrame frame = new SecurityContextFrame();
frame.Push(decoratedModule.GetRuntimeAssembly());
// Optimization for the case where attributes decorate entities in the same assembly in which case
// we can cache the successful APTCA check between the decorated and the declared assembly.
Assembly lastAptcaOkAssembly = null;
for (int i = 0; i < car.Length; i++)
{
object attribute = null;
CustomAttributeRecord caRecord = car[i];
IRuntimeMethodInfo ctor = null;
RuntimeType attributeType = null;
bool ctorHasParameters, isVarArg;
int cNamedArgs = 0;
IntPtr blobStart = caRecord.blob.Signature;
IntPtr blobEnd = (IntPtr)((byte*)blobStart + caRecord.blob.Length);
int blobLen = (int)((byte*)blobEnd - (byte*)blobStart);
if (!FilterCustomAttributeRecord(caRecord, scope, ref lastAptcaOkAssembly,
decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable,
attributes, derivedAttributes,
out attributeType, out ctor, out ctorHasParameters, out isVarArg))
continue;
if (ctor != null)
{
// Linktime demand checks
// decoratedMetadataToken needed as it may be "transparent" in which case we do a full stack walk
RuntimeMethodHandle.CheckLinktimeDemands(ctor, decoratedModule, isDecoratedTargetSecurityTransparent);
}
else
{
//
}
// Leverage RuntimeConstructorInfo standard .ctor verfication
RuntimeConstructorInfo.CheckCanCreateInstance(attributeType, isVarArg);
// Create custom attribute object
if (ctorHasParameters)
{
attribute = CreateCaObject(decoratedModule, ctor, ref blobStart, blobEnd, out cNamedArgs);
}
else
{
attribute = RuntimeTypeHandle.CreateCaInstance(attributeType, ctor);
// It is allowed by the ECMA spec to have an empty signature blob
if (blobLen == 0)
cNamedArgs = 0;
else
{
// Metadata is always written in little-endian format. Must account for this on
// big-endian platforms.
#if BIGENDIAN
const int CustomAttributeVersion = 0x0100;
#else
const int CustomAttributeVersion = 0x0001;
#endif
if (Marshal.ReadInt16(blobStart) != CustomAttributeVersion)
throw new CustomAttributeFormatException();
blobStart = (IntPtr)((byte*)blobStart + 2); // skip version prefix
cNamedArgs = Marshal.ReadInt16(blobStart);
blobStart = (IntPtr)((byte*)blobStart + 2); // skip namedArgs count
#if BIGENDIAN
cNamedArgs = ((cNamedArgs & 0xff00) >> 8) | ((cNamedArgs & 0x00ff) << 8);
#endif
}
}
for (int j = 0; j < cNamedArgs; j++)
{
#region // Initialize named properties and fields
string name;
bool isProperty;
RuntimeType type;
object value;
IntPtr blobItr = caRecord.blob.Signature;
GetPropertyOrFieldData(decoratedModule, ref blobStart, blobEnd, out name, out isProperty, out type, out value);
try
{
if (isProperty)
{
#region // Initialize property
if (type == null && value != null)
{
type = (RuntimeType)value.GetType();
if (type == Type_RuntimeType)
type = Type_Type;
}
RuntimePropertyInfo property = null;
if (type == null)
property = attributeType.GetProperty(name) as RuntimePropertyInfo;
else
property = attributeType.GetProperty(name, type, Type.EmptyTypes) as RuntimePropertyInfo;
// Did we get a valid property reference?
if (property == null)
{
throw new CustomAttributeFormatException(
String.Format(CultureInfo.CurrentUICulture, Environment.GetResourceString(
isProperty ? "RFLCT.InvalidPropFail" : "RFLCT.InvalidFieldFail"), name));
}
RuntimeMethodInfo setMethod = property.GetSetMethod(true) as RuntimeMethodInfo;
// Public properties may have non-public setter methods
if (!setMethod.IsPublic)
continue;
RuntimeMethodHandle.CheckLinktimeDemands(setMethod, decoratedModule, isDecoratedTargetSecurityTransparent);
setMethod.UnsafeInvoke(attribute, BindingFlags.Default, null, new object[] { value }, null);
#endregion
}
else
{
RtFieldInfo field = attributeType.GetField(name) as RtFieldInfo;
if (isDecoratedTargetSecurityTransparent)
{
RuntimeFieldHandle.CheckAttributeAccess(field.FieldHandle, decoratedModule.GetNativeHandle());
}
field.CheckConsistency(attribute);
field.UnsafeSetValue(attribute, value, BindingFlags.Default, Type.DefaultBinder, null);
}
}
catch (Exception e)
{
throw new CustomAttributeFormatException(
String.Format(CultureInfo.CurrentUICulture, Environment.GetResourceString(
isProperty ? "RFLCT.InvalidPropFail" : "RFLCT.InvalidFieldFail"), name), e);
}
#endregion
}
if (!blobStart.Equals(blobEnd))
throw new CustomAttributeFormatException();
attributes[cAttributes++] = attribute;
}
// The frame will be popped automatically if we take an exception any time after we pushed it. So no need of a catch or
// finally or CERs here.
frame.Pop();
if (cAttributes == car.Length && pcaCount == 0)
return attributes;
object[] result = CreateAttributeArrayHelper(arrayType, cAttributes + pcaCount);
Array.Copy(attributes, 0, result, 0, cAttributes);
return result;
}
异常的消息肯定没有帮助,因为它没有给出根本原因!