Why are C# attributes so limited?
I understand that they are compile time, so they can't be generic, and must be initialized with constant values. But:
Why can't they have information you would get if you reflected whatever they are being applied to? Why can't they accept lambda expressions, functions, or delegates? Aren't functions constant to the compiler?Attributes could be a phenomenally powerful declarative tool if just one of the above were true, instead they are more like comments that can be read through reflection.
This was sort of a rant, but I really want to know why they seem like such a half assed feature.
Here is what I wanted to do. It was supposed to be an api for mapping values from resources through the function given to the attribute to the property the attribute is applied to. Note that the abstract class wouldn't have to exist if Attributes could know what they are reflecting. I'm posting this because someone wanted to know why I would want to give functions to attribute constructors, and maybe because what I'm trying to do has already been done.
public delegate void PropertyHandler(object parent, PropertyInfo property, object value);
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class FromResourceAttribute : Attribute
{
private static readonly PropertyHandler m_defaultHandler = (parent, property, value) =>
{
property.SetValue(parent, value, null);
};
public PropertyHandler Handler { get; set; }
public FromResourceAttribute(PropertyHandler handler)
{
Handler = handler;
}
public FromResourceAttribute()
{
Handler = m_defaultHandler;
}
}
public abstract class ResourceDependent
{
public ResourceDependent(ResourceManager resources)
{
var resourceDependentProperties =
from property in GetType().GetProperties()
let fromResourceAttributes = property.GetC开发者_开发问答ustomAttributes(typeof(FromResourceAttribute), true)
where fromResourceAttributes.Count() == 1
let propertyHandler = ((FromResourceAttribute)fromResourceAttributes.Single()).Handler
select new { Info = property, Handler = propertyHandler };
foreach(var property in resourceDependentProperties)
{
property.Handler(this, property.Info, resources.GetObject(property.Info.Name));
}
}
}
class ResourceDependentTest : ResourceDependent
{
[FromResource]
public string Data { get; set; }
[FromResource((parent, property, value) => property.SetValue(parent, ((string)value).Split('|'), null))]
public string[] Data2 { get; set; }
static PropertyHandler Data3Handler = (parent, property, value) =>
{
//Magic
};
[FromResource(Data3Handler)]
public int Data3 { get; set; }
public ResourceDependentTest() : base(Properties.Resources.ResourceManager)
{
}
}
Why can't they have information you would get if you reflected whatever they are being applied to?
Part of the reason is attributes don't have to be applied to anything. It's perfectly legal to new up an attribute in imperative code which is not attached to anything.
// Not attached
var attrib = new CLSCompliantAttribute(false);
Why can't they accept lambda expressions, functions, or delegates? Aren't functions constant to the compiler?
All of these resolve down to the question of why delegates cannot be a part of an attribute. At their core delegates are made up of two parts 1) an instance and 2) a pointer to a method. #1 pretty much kills it as the instance is not constant and can't be a part of the attribute value.
There are several other case which exist with delegates including delegate's to static methods and expression trees. These have similar issues though in that all parts of the expression are not constant hence not encodable in an attribute value.
Actually the question about why they can't be generic has been answered before, and according to Eric Lippert they are not supported simply because they'd add complexity to the language and it's just not been considered worth doing so far. It's certainly not because it's compile time. In IL you apparently can make generic attributes. (Reference.)
As for why they can't take lambda expressions and so forth, I imagine it's the same reason. The request for delegates to be used has been Microsoft Connect since 2006. You might vote for it if you like.
The short, short, version, is that IL doesn't support more functionality; see ECMA-335 Partition II §21 Custom attributes and Partition II §23.3 Custom attributes.
To summarize §23.3, custom attributes use a custom serialization format that is stored within IL, not Binary Serialization, not XML or SOAP serialization. Quoting from the standard §23.3:
The FieldOrPropType shall be exactly one of:
ELEMENT_TYPE_BOOLEAN
,ELEMENT_TYPE_CHAR
,ELEMENT_TYPE_I1
,ELEMENT_TYPE_U1
,ELEMENT_TYPE_I2
,ELEMENT_TYPE_U2
,ELEMENT_TYPE_I4
,ELEMENT_TYPE_U4
,ELEMENT_TYPE_I8
,ELEMENT_TYPE_U8
,ELEMENT_TYPE_R4
,ELEMENT_TYPE_R8
,ELEMENT_TYPE_STRING
. A single-dimensional, zero-based array is specified as a single byte 0x1D followed by the FieldOrPropType of the element type. (See §23.1.16) An enum is specified as a single byte 0x55 followed by a SerString.followed by a SerString.
The obvious response, of course, is "why doesn't the IL serialization format support method references/etc. And the obvious answer, of course, is that they felt it wasn't necessary and/or it would further bloat IL.
(An interesting aside: Type references are actually implemented by using a string containing the fully-qualified assembly name of the type. By the same logic, I suppose a method reference could be "encoded" by using the type name and an encoding for the method, but I suspect this would be "brittle", in that adding/changing methods could break things.)
Im not very into attributes (because in my opinion they are made for tool builders like nunit), but i think you already answered your question: Attributes are compile time constant
Attribute parameters are restricted to constant values of the following types:
* Simple types (bool, byte, char, short, int, long, float, and double) * string * System.Type * enums * object (The argument to an attribute parameter of type object
must be a constant value of one of the above types.) * One-dimensional arrays of any of the above types
http://msdn.microsoft.com/en-us/library/aa288454%28VS.71%29.aspx#vcwlkattributestutorialanchor1
while the returned value of lambdas, delegates and functions are not.
I dont really want to know how code with heavy lambda use in attributes would look like, should be quite a mess.
精彩评论