I am confused, because in C#, a decimal literal such as 1.0m
can be used as a constant field, but not as an argument to an attribute.
[Foo(1.0m)] // Error
class C
{
const decimal constD1 = 1.0m;
}
In a thread at MSDN, James Curran replied:
"1000M" is merely shorthand for "new Decimal(1000)", which involves a method call, which means it's not considered a constant. Just because the compile lets you pretend it's a constant most of the time, doesn't mean you can all of the time.
But if it is a shorthand for a constructor call, you should be able to do this:
class C
{
const decimal constD2 = new decimal(1.0); // Error
}
If I understand Jon Skeet's reply to a similar question correctly, it is impossible to pass a decimal to an attribute, which means, I can never successfully do something like this:
class FooAttribute : Attribute
{
public FooAttribute(object obj)
{
if (obj is decimal)
{
// Doomed to be dead code?
}
}
}
Also see this question. By the way, ReSharper does not show a compiler error for a decimal constant/literal in an attribute value, which is a known issue.
Here is a full example:
namespace ClassLibrary1
{
[Foo("")]
[Foo(new string(new char[] { }))] // Error 1
[Foo(1.0m)] // Error 1 (no error in ReSharper)
[Foo(new decimal(1.0))] // Error 1
class C
{
static readonly string s1 = "";
static readonly string s2 = new string(new char[] {});
static readonly decimal d1 = 1.0m;
static readonly decimal d2 = new decimal(1.0);
const string constS1 = "";
const string constS2 = new string(new char[] {}); // Error 2
const decimal constD1 = 1.0m;
const decimal constD2 = new decimal(1.0); // Error 2
// Error 1:
// An attribute argument must be a constant expression, typeof expression or
// array creation expression of an attribute parameter type
// Error 2:
// The expression being assigned to (...) must be constant
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
class FooAttribute : Attribute
{
public FooAttribute(object obj)
{
if (obj is decimal)
{
// Doomed to be dead code?
}
}
}
}
So, what is this thing "1.0m" actually? And how can I work around the fact that I cannot use it as an argument to an attribute? The only solution I can think of is to sacrify type safety and do this if dealing with a custom attribute:
[Bar("1.0")]
class C
{
}
class BarAttribute : Attribute
{
public BarAttribute(string decimalAsString)
{
decimal d = Convert.ToDecimal(decimalAsString);
...
}
}
When dealing with NUnit's TestCaseAttribute
, I would do something similar. According to NUnit docs, NUnit converts the type automatically.
[TestCase("1.0")]
public void Test(decimal d)
{
...
}