Why "Decimal" Is Not a Valid Attribute Parameter Type

Why decimal is not a valid attribute parameter type?

This is a CLR restriction. Only
primitive constants or arrays of
primitives can be used as attribute
parameters. The reason why is that an
attribute must be encoded entirely in
metadata. This is different than a
method body which is coded in IL.
Using MetaData only severely restricts
the scope of values that can be used.
In the current version of the CLR,
metadata values are limited to
primitives, null, types and arrays of
primitives (may have missed a minor
one).

Taken from this answer by JaredPar.

Decimals while a basic type are not a
primitive type and hence cannot be
represented in metadata which prevents
it from being an attribute parameter.

use decimal values as attribute params in c#?

This is a CLR restriction. Only primitive constants or arrays of primitives can be used as attribute parameters. The reason why is that an attribute must be encoded entirely in metadata. This is different than a method body which is coded in IL. Using MetaData only severely restricts the scope of values that can be used. In the current version of the CLR, metadata values are limited to primitives, null, types and arrays of primitives (may have missed a minor one).

Decimals while a basic type are not a primitive type and hence cannot be represented in metadata which prevents it from being an attribute parameter.

Why C# Attribute using params doesn't work with my class but work with strings?

The parameters to an Attribute constructor must be constants, or Types, or 1-D Arrays of them.

You can't use anything that's merely static readonly. So you're mostly limited to string, number literals and constants, enums and Type. You can't even use Decimal.

A workaround is for the parameters to your Attribute constructor to be the parameters you can use in your Attribute constructor's code to create its own instance.

NB another common gotcha with Attributes is that they sometimes turn out to be singletons-per-set-of-parameters. You get little control of Attributes.

An expression E is an attribute_argument_expression if all of the following statements are true:

  • The type of E is an attribute parameter type.
  • At compile-time, the value of E can be resolved to one of the following:
    • A constant value.
    • A System.Type object.
    • A one-dimensional array of attribute_argument_expressions.

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/attributes

and

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.
    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.

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/attributes#attribute-parameter-types

How does one work around the decimal attribute parameter issue?

Went with using a string to handle the rounding issues as Hans mentioned.

If anyone has a better idea, please answer this question and I will mark it.

Why is a NullableT not a valid Custom Attribute Parameter when T is?

Hungry? is equal to Nullable<Hungry>, which in terms mean that

[Hunger(NullableHungerLevel = Hungry.CouldEatMySocks)]

is equal to

[Hunger(NullableHungerLevel = new Nullable<Hungry>(Hungry.CouldEatMySocks))]

Since you can only use constant values in named attribute arguments you will have to resort to Shimmy's solution.

Custom Attribute - getting not a valid named attribute argument for an enum

The problem is that the MultiselectComperer property is nullable. The compiler is complaining because unfortunately you can't make a constant of a nullable type. If you make it non nullable, your class will work just fine.

If you need to represent an third value to the eMultiselectComperer, enum other than Or and And you can create a third enum value as the default value for that enum, like this:

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
public sealed class FilterAttribute : Attribute
{
public enum eMultiselectComperer
{
Unspecified = 0,
Or,
And
}

public bool IsMultiselect { get; set; }

public eMultiselectComperer MultiselectComperer { get; set; }
}

This way, if the user doesn't specify a value for the MultiselectComperer property when declaring the attribute, it will default to Unspecified (or whatever you prefer to call it).

Validate a decimal value

You can use "Range" annotation;
10.50D is min value,50.80D is max value...

[Range(10.50D,50.80D,ErrorMessage ="Error min")]
public decimal Salary { get; set; }

Range attribute specifies the numeric range constraints for the value of a data field. It comes with System.ComponentModel.DataAnnotations (in System.ComponentModel.DataAnnotations.dll)

Having an actual decimal value as parameter for an attribute (example xUnit.net's [InlineData]

You should be able use the String value in the Attribute and set the Parameter type to Decimal, it get's converted automatically by the Test Framework as far as I can tell.

[Theory]
[InlineData("37.60")]
public void MyDecimalTest(Decimal number)
{
Assert.Equal(number, 37.60M);
}

If this doesn't work then you can manually convert it by passing in a String parameter.

[Theory]
[InlineData("37.60")]
public void MyDecimalTest(String number)
{
var d = Convert.ToDecimal(number);
Assert.Equal(d, 37.60M);
}

Passing Func as an attribute parameter to secure MVC routes

Necros' suggestion would work, however you would have to invoke his SecurityGuard helper in the body of every action method.

If you would still like to go with the declarative attribute-based approach (which has the advantage that you can apply the attribute to the whole Controller) you could write your own AuthorizeAttribute

public class CustomAuthorizeAttribute : AuthorizeAttribute {
public bool EmployeeOnly { get; set; }
private string _company;

public string Company {
get { return _company; }
set { _company = value; }
}

protected override bool AuthorizeCore(HttpContextBase httpContext) {
return base.AuthorizeCore(httpContext) && MyAuthorizationCheck(httpContext);
}

private bool MyAuthorizationCheck(HttpContextBase httpContext) {
IPrincipal user = httpContext.User;

if (EmployeeOnly && !VerifyUserIsEmployee(user)) {
return false;
}

if (!String.IsNullOrEmpty(Company) && !VerifyUserIsInCompany(user)) {
return false;
}

return true;
}

private bool VerifyUserIsInCompany(IPrincipal user) {
// your check here
}

private bool VerifyUserIsEmployee(IPrincipal user) {
// your check here
}
}

Then you would use it as follows

[CustomAuthorize(Company = "Acme")]   
public ActionResult AcmeOnlyAction()
{
...
}

[CustomAuthorize(EmployeeOnly = true)]
public ActionResult EmployeeOnlyAction()
{
...
}

Why is decimal not a primitive type?

Although not a direct answer, the documentation for IsPrimitive lists what the primitive types are:

http://msdn.microsoft.com/en-us/library/system.type.isprimitive.aspx

A similar question was asked here:

http://bytes.com/topic/c-sharp/answers/233001-typeof-decimal-isprimitive-false-bug-feature

Answer quoted from Jon Skeet:

The CLR doesn't need to have any intrinsic knowledge about the decimal
type - it treats it just as another value type which happens to have
overloaded operators. There are no IL instructions to operate directly
on decimals, for instance.

To me, it seems as though decimal is a type that must exist for a language/runtime wanting to be CLS/CLI-compliant (and is hence termed "primitive" because it is a base type with keyword support), but the actual implementation does not require it to be truly "primitive" (as in the CLR doesn't think it is a primitive data type).



Related Topics



Leave a reply



Submit