60

I am attempting to pass objects into an Attributes constructor as follows:

[PropertyValidation(new NullOrEmptyValidatorScheme())]
public string Name { get; private set; }

With this attribute constructor:

 public PropertyValidationAttribute(IValidatorScheme validator) {
      this._ValidatorScheme = validator;
    }

The code won't compile. How can I pass an object into an attribute as above?

EDIT: Yes NullOrEmptyValidatorScheme implements IValidatorScheme.

The error: error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
theringostarrs
  • 11,940
  • 14
  • 50
  • 63

3 Answers3

73

The values into attributes are limited to simple types; for example, basic constants (including strings) and typeof... you can't use new or other more complex code. In short; you can't do this. You can give it the type though:

[PropertyValidation(typeof(NullOrEmptyValidatorScheme)]

i.e. the PropertyValidation ctor takes a Type, and use Activator.CreateInstance inside the code to create the object. Note that you should ideally just store the string internally (AssemblyQualifiedName).

From ECMA 334v4:

§24.1.3 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, short, string.
  • 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.
  • Single-dimensional arrays of the above types.

and

§24.2 Attribute specification

...

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 (§24.1.3).
  • At compile-time, the value of E can be resolved to one of the following:
    • A constant value.
    • A typeof-expression (§14.5.11) specifying a non-generic type, a closed constructed type (§25.5.2), or an unbound generic type (§25.5).
    • A one-dimensional array of attribute-argument-expressions.
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • This is the second reference to one of allowed values being an enum, however implementing that has not been trivial. Do you know of any examples implementations using enum? – QueueHammer Dec 12 '16 at 15:53
  • @QueueHammer `[DefaultValue(AnyEnum.SomeValue)]` should suffice; otherwise, something like `[System.Xml.Serialization.XmlElement(Form = System.Xml.Schema.XmlSchemaForm.Qualified)]` – Marc Gravell Dec 12 '16 at 16:02
10

As previous posters noted, the types use in attribute arguments are quite severely restricted (understandably, because their values need to be serialized directly into the assembly metadata blob).

That said, you can probably create a solution that utilizes typeofs, as those can be used.

For instance :

[PropertyValidation(typeof(NullOrEmptyValidatorScheme))]
public string Name { get; private set; }

This syntax is perfectly legal. The code that reads your attributes you have to get the validator type, create a new instance of the validator (it can even maintain a cache of validators keyed on valicator types, if appropriate - this is a fairly common technique), and then invoke it.

  • Thanks for you answer. I have given Marc the accepted answer. But your suggestion of using a cache is helpful, and had envisioned doing this to save from many Activator.CreateInstance calls. – theringostarrs Aug 06 '09 at 01:51
5

Also... (I think it is a Microsoft Bug)

You can't put a default value to "null" but default simple default value are ok ('false', '7', '"Test").

NExt example will give you the following error: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
in file: ... \CSC

public class SampleAttribute : Attribute
{
    private string _test;
    public SampleAttribute(string test = null)
    {
        _test = test;
    }
}

[Sample]
public class Toto
{

}
Eric Ouellet
  • 10,996
  • 11
  • 84
  • 119
  • Might be related: "Attributes and Named/Optional constructor parameters not working" http://stackoverflow.com/q/8189807/276648 – user276648 Dec 05 '12 at 09:12
  • To user276648, I think you are right, they are related and solution there seems to be more complete. Thanks ! – Eric Ouellet Dec 05 '12 at 13:28
  • Actually with your sample it might be related to a compiler bug (meaning what you wrote may work when compiled by Mono) http://stackoverflow.com/q/8290853 – user276648 Dec 06 '12 at 02:32