Attribute help with Routing, compiler error - c#

I have created a custom attribute called RouteAttribute:
[AttributeUsage(AttributeTargets.Property)]
public class RouteAttribute : Attribute
{
public string Url { get; set; }
public bool CheckPhysicalUrlAccess { get; set; }
public RouteValueDictionary Defaults { get; set; }
public RouteValueDictionary Constraints { get; set; }
public RouteValueDictionary DataTokens { get; set; }
}
It is used to add routing via attribute on my url helper class that contains a list of urls in my site, so i have a easy way to manage my site urls.
Having a problem with adding a default though, getting compiler error:
[Route("~/MyPage/Home.aspx", new RouteValueDictionary { { "query", "value" } })]
public string HomePage
{
get { return "Home" }
}
To avoid confusion, the value is set to the routeurl, physical url is from attribute,
reason for this is, I am converting an existing site, and rather than changing links everywhere, once I'm done with page, I go to my class and change the physical url to new page
Giving an error:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type

The argument values for an attribute constructor are stored in metadata. That puts severe restrictions on what you can specify. Just simple value types, a Type from typeof and a simple one dimensional array of these values. No code is allowed, which is what the compiler complains about, the new operator requires code.
There are no constrictions on what you can do in the body of the attribute constructor, that code runs later when reflection code inspects the attribute. Suggesting something similar to this:
public class RouteAttribute : Attribute
{
public RouteAttribute(string url, string query, string value) {
this.url = url;
this.dunno = new RouteValueDictionary(query, value);
}
// etc..
}
...
[Route("~/MyPage/Home.aspx", "query", "value")]
public string HomePage
{
get { return "Home" }
}
This obviously needs work, I have no idea what the dictionary means. Be careful about it having side-effects or requiring resources, you don't know the runtime state when the attribute gets constructed.

An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
The error tells you exactly what the problem is.
As
new RouteValueDictionary { { "query", "value" } }
is not a constant expression, not a typeof expression and not an array creation expression, this is not legal.

Related

C# Method/Function Parameter Passing Similar to Named Parameters, But Using '=' instead of ':' [duplicate]

I have custom attribute defined like so:
[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayAttribute : Attribute
{
public string Description { get; private set; }
public string Code { get; private set; }
public EnumDisplayAttribute(string description = null, string code = null)
{
Description = description;
Code = code;
}
}
Both constructor parameters are optional.
When using this attribute on a field like so
public enum TransactionType
{
[EnumDisplay(code: "B")]
Bill,
[EnumDisplay(description: null, code: "C")]
CashReceipt,
}
I don't see any squigglies in the code editor but I see a vague error without any File Line number of column. The error message is:
error CS0182: An attribute argument must be a constant expression, typeof expression
or array creation expression of an attribute parameter type
Clicking on the error does nothing. That is, you don't get navigated to the error site (obviously, since there is no line number and column).
even if I set up the attribute like so:
[EnumDisplay("This is a Bill")]
The compiler doesn't like it.
Effectively, I am forced to provide both parameters (named or not) in order to use this attribute as an attribute.
Of course if I use this attribute as a regular class like so:
var enumDisplayAttribute = new EnumDisplayAttribute();
enumDisplayAttribute = new EnumDisplayAttribute(description: "This is a Bill");
enumDisplayAttribute = new EnumDisplayAttribute(code: "B");
enumDisplayAttribute = new EnumDisplayAttribute(description: "This is a Bill", code: "B");
enumDisplayAttribute = new EnumDisplayAttribute("This is a Bill", "B");
enumDisplayAttribute = new EnumDisplayAttribute("This is a Bill");
The compiler will accept any one of the above "styles".
Surely, I'm missing something or my brain is just not working.
Optional parameters were added to C# after optional values for attributes already existed in C#. Therefore, for optional attribute parameters, you should fall back to the attribute-specific syntax:
[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayAttribute : Attribute
{
public string Description { get; set; }
public string Code { get; set; }
public EnumDisplayAttribute()
{
}
}
public enum TransactionType
{
[EnumDisplay(Code = "B")]
Bill,
[EnumDisplay(Description = null, Code = "C")]
CashReceipt,
}
As you see, the end-result is effectively the same, but instead of using named arguments, you are using named properties (where syntax like [EnumDisplay(Description = null, Code = "C")] is only possible in attribute declarations).
Another way to think of it is that attribute declarations "borrowed" its syntax from method/constructor invocations, but attribute declarations are not in themselves method invocations, so they don't get all the same features as methods.
If you do want to push values into your attribute using a constructor (e.g. if some of your attribute's properties are mandatory or to perform some kind of processing on them) you can always go old school and overload the constructor.
For example:
[AttributeUsage(AttributeTargets.Field)]
public class SampleAttribute : Attribute
{
public string MandatoryProperty { get; private set; }
public string OptionalProperty { get; private set; }
// we use an overload here instead of optional parameters because
// C# does not currently support optional constructor parameters in attributes
public SampleAttribute(string mandatoryProperty)
: this(mandatoryProperty, null)
{
}
public SampleAttribute(string mandatoryProperty, string optionalProperty)
{
MandatoryProperty = mandatoryProperty;
OptionalProperty = optionalProperty;
}
}
Optional parameters are not really optional, the method signature has all arguments in it and attributes are special (existed before optional parameters and have different rules when applied as an attribute (eg consider who calls the attribute constructor)). I imagine however that support will be added in the future.
For now, if you wish to achieve the optional effect try the following:
[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayAttribute : Attribute
{
public string Description { get; set; }
public string Code { get; set; }
}
And apply as so:
[EnumDisplay(Description = null, Code = "C")]
private object _aField;

Localizing string properties of Attributes in c#

Localizing properties of the class are out of box by using the DisplayAttribute.
When trying to localize Attributes using a resource file EmployeeResx.resx , EmployeeResx.fr.res...., static class EmployeeResx.Designer.cs is generated with static string properties like:
public static string LastName {
get {
return ResourceManager.GetString("LastName", resourceCulture);
}
}
Trying to use the static string to localize the properties of the Attributes (Option in this example), like:
[Option('l', "lastname", HelpText = EmployeeResx.LastName)]
public string LastName { get; set; }
c# compiler raise error:
Error CS0182 An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
There are many attributes need to be localized.
How to localize the string properties of the Attributes like the above example?
Make a derived attribute type. Pass the resource name into the derived attribute class. The derived attribute class can retrieve the resource string and pass it into the constructor of the base class.
If your Option attribute has a single string parameter, the derived class would be something like this.
internal class localized_OptionAttribute : OptionAttribute
{
public localized_Option ( string ResourceName )
: base ( <root namespace>.Properties.Resources.ResourceManager.GetString ( ResourceName ) )
{
}
}
Then you can use the new attribute in place of the original one:
[localized_Option("LastName")]
public string LastName { get; set; }
where "LastName" is now used as the resource name.
It looks like your attribute has some additional parameters, which you would have to define as additional parameters to the constructor of the derived class. I have left them out for simplicity.

Map enum to json property

I have a class called InstrumentConfigValues with properties that has type implementing an interface. Now I have an enum by name InstrumentConfig which has set of values. These values are like keys inside the json file. I want to map something like [JsonProperty(InstrumentConfig.LowDiskpace.ToString()].
For some reason its not allowing this and complains saying:
An attribute argument must be constant expression
I referred to many post specifically JsonStringEnumConverter. But how can I map each property with the enum key. I also saw this post JsonSerializationSettings but not able to correlate to my problem. Please help/
public class InstrumentConfigValues : IInstrumentConfig
{
public double SpaceNeededForSingleRun
{
get; set;
}
public int NumberOfInputSlots
{
get; set;
}
public int SupportedChannelCount
{
get; set;
}
}
//I want this inheritance as some other class wants to access the values.
public abstract class InstrumentConfigReadWrite : InstrumentConfigValues
{
protected ReturnCodes PopulateValuesFromJObject(JObject jObject, string path)
{
try
{
if (JsonConvert.DeserializeObject<InstrumentConfigValues>(jObject.ToString()) == null)
{
return ReturnCodes.ErrorReadingFile;
}
}
catch (JsonSerializationException jex)
{
SystemDebugLogLogger.LogException(jex, "Invalid Instrument Config File Values. Data needs to be copied over.");
return ReturnCodes.ErrorReadingFile;
}
return ReturnCodes.Success;
}
}
As long as you're using a current compiler, you can use nameof.
[JsonProperty(nameof(InstrumentConfig.LowDiskpace))]
If you try using this, and get an error like Compilation error: The name 'nameof' does not exist in the current context, that means you're not using a current compiler. The nameof keyword was introduced in C# 6.0/Visual Studio 2015--anything newer than that should be fine.

How to resolve : An attribute argument must be a constant expression error? [duplicate]

Here is my custom attribute and a class I'm using it on:
[MethodAttribute(new []{new MethodAttributeMembers(), new MethodAttributeMembers()})]
public class JN_Country
{
}
public class MethodAttribute : Attribute
{
public MethodAttributeMembers[] MethodAttributeMembers { get; set; }
public MethodAttribute(MethodAttributeMembers[] methodAttributeMemberses)
{
MethodAttributeMembers = methodAttributeMemberses;
}
}
public class MethodAttributeMembers
{
public string MethodName { get; set; }
public string Method { get; set; }
public string MethodTitle { get; set; }
}
The syntax error, displayed on the first line above:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
Why does this error occur?
This supplements the information Simon already gave.
I found some documentation here: Attribute parameter types:
The types of positional and named parameters for an attribute class are limited to the attribute parameter types, which are:
One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
The type object.
The type System.Type.
An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility (Attribute specification).
Single-dimensional arrays of the above types. (emphasis added by me)
A constructor argument or public field which does not have one of these types, cannot be used as a positional or named parameter in an attribute specification.
The last bullet point explains your syntax error. You've defined a one-dimensional array, but it should only be of primitive types, string, etc. as listed in the previous bullet points.
Attribute arguments must be compile-time constant. That means that the compiler must be able to "bake in" the values of the arguments when the assembly is compiled. new ReferenceType() is not constant - it must be evaluated at runtime to determine what it is.
Interestingly, this is a little bit flimsy in that there are some edge cases to that rule. Other than those though, you cannot do what you're trying to do.
Let me add that the compiler can return this error without any particular file or line of code, if your attribute has a constructor that has a parameter that isn't a simple type and you use the constructor (i.e. your non-simple parameter has a default value).
[MyAttribute(MySimpleParameter: "Foo")]
public class MyObject
{
}
public class MyAttribute : Attribute
{
public string MySimpleProperty { get; set; }
public MyPropertyClass MyComplexProperty { get; set; }
public MethodAttribute(string MySimpleParameter, MyPropertyClass MyComplexParameter = null)
{
MySimpleProperty = MySimpleParameter;
MyComplexProperty = MyComplexParameter;
}
}
public class MyPropertyClass
{
public string Name { get; set; }
public string Method { get; set; }
}

Attributes and Named/Optional constructor parameters not working

I have custom attribute defined like so:
[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayAttribute : Attribute
{
public string Description { get; private set; }
public string Code { get; private set; }
public EnumDisplayAttribute(string description = null, string code = null)
{
Description = description;
Code = code;
}
}
Both constructor parameters are optional.
When using this attribute on a field like so
public enum TransactionType
{
[EnumDisplay(code: "B")]
Bill,
[EnumDisplay(description: null, code: "C")]
CashReceipt,
}
I don't see any squigglies in the code editor but I see a vague error without any File Line number of column. The error message is:
error CS0182: An attribute argument must be a constant expression, typeof expression
or array creation expression of an attribute parameter type
Clicking on the error does nothing. That is, you don't get navigated to the error site (obviously, since there is no line number and column).
even if I set up the attribute like so:
[EnumDisplay("This is a Bill")]
The compiler doesn't like it.
Effectively, I am forced to provide both parameters (named or not) in order to use this attribute as an attribute.
Of course if I use this attribute as a regular class like so:
var enumDisplayAttribute = new EnumDisplayAttribute();
enumDisplayAttribute = new EnumDisplayAttribute(description: "This is a Bill");
enumDisplayAttribute = new EnumDisplayAttribute(code: "B");
enumDisplayAttribute = new EnumDisplayAttribute(description: "This is a Bill", code: "B");
enumDisplayAttribute = new EnumDisplayAttribute("This is a Bill", "B");
enumDisplayAttribute = new EnumDisplayAttribute("This is a Bill");
The compiler will accept any one of the above "styles".
Surely, I'm missing something or my brain is just not working.
Optional parameters were added to C# after optional values for attributes already existed in C#. Therefore, for optional attribute parameters, you should fall back to the attribute-specific syntax:
[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayAttribute : Attribute
{
public string Description { get; set; }
public string Code { get; set; }
public EnumDisplayAttribute()
{
}
}
public enum TransactionType
{
[EnumDisplay(Code = "B")]
Bill,
[EnumDisplay(Description = null, Code = "C")]
CashReceipt,
}
As you see, the end-result is effectively the same, but instead of using named arguments, you are using named properties (where syntax like [EnumDisplay(Description = null, Code = "C")] is only possible in attribute declarations).
Another way to think of it is that attribute declarations "borrowed" its syntax from method/constructor invocations, but attribute declarations are not in themselves method invocations, so they don't get all the same features as methods.
If you do want to push values into your attribute using a constructor (e.g. if some of your attribute's properties are mandatory or to perform some kind of processing on them) you can always go old school and overload the constructor.
For example:
[AttributeUsage(AttributeTargets.Field)]
public class SampleAttribute : Attribute
{
public string MandatoryProperty { get; private set; }
public string OptionalProperty { get; private set; }
// we use an overload here instead of optional parameters because
// C# does not currently support optional constructor parameters in attributes
public SampleAttribute(string mandatoryProperty)
: this(mandatoryProperty, null)
{
}
public SampleAttribute(string mandatoryProperty, string optionalProperty)
{
MandatoryProperty = mandatoryProperty;
OptionalProperty = optionalProperty;
}
}
Optional parameters are not really optional, the method signature has all arguments in it and attributes are special (existed before optional parameters and have different rules when applied as an attribute (eg consider who calls the attribute constructor)). I imagine however that support will be added in the future.
For now, if you wish to achieve the optional effect try the following:
[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayAttribute : Attribute
{
public string Description { get; set; }
public string Code { get; set; }
}
And apply as so:
[EnumDisplay(Description = null, Code = "C")]
private object _aField;

Categories