5

I recently discovered that a .NET attribute can only contain primitive types, strings, enums, objects, and single parameter arrays of these types as discussed here.

I have the need to accept an IDictionary<string, string> through a .NET attribute, but obviously I can't use IDictionary<string, string> for the above reason. So, I am here looking for alternatives that can work within these guidelines.

// I want to do this, but can't because of the CLR limitation
[MyAttribute(Attributes = new Dictionary { { "visibility", "Condition1" }, { "myAttribute", "some-value" } })]

Two possible options I came up with are to use XML or JSON in the declaration (as a string on the .NET attribute), which can be easily serialized into IDictionary<string, string> for my application, but this turns the declaration into a lengthy error-prone string with no syntax checking.

// XML
[MyAttribute(Attributes = "<items><item key='visibility' value='Condition1'/><item key='myAttribute' value='some-value'/></items>")]

// JSON
[MyAttribute(Attributes = "{ 'visibility': 'Condition1', 'myAttribute': 'some-value' }")]

What, if any, other alternatives are available that are more elegant/straightforward than this?

Community
  • 1
  • 1
NightOwl888
  • 55,572
  • 24
  • 139
  • 212

4 Answers4

11

As far as I know, you can add an attribute several times (Edit: If you set AllowMultiple to true, as Robert Levy noted). So maybe instead of using one attribute with a whole dictionary, you could use several attributes, one for each dictionary entry, each with a key and a value parameter.

[MyDictionaryEntry(Key = key1, Value = val1)]
[MyDictionaryEntry(Key = key2, Value = val2)]
[MyDictionaryEntry(Key = key3, Value = val3)]
[MyDictionaryEntry(Key = key4, Value = val4)]
Sebastian Negraszus
  • 11,915
  • 7
  • 43
  • 70
  • Interesting idea. Unfortunately, this would mean I would have to make this one property of my existing custom attribute into a separate custom attribute - and then they would need to be linked together. Not to mention, it is not that intuitive because every other place in the application accepts a Dictionary as a property. Unless I have missed something...? Here is what I need to fix: https://github.com/maartenba/MvcSiteMapProvider/blob/master/src/MvcSiteMapProvider/MvcSiteMapProvider/MvcSiteMapNodeAttribute.cs – NightOwl888 Jan 21 '14 at 18:10
2

You can define an attribute that takes 2 string parameters (key and value) and AllowMultiple=true so that multiple instances of it can be applied to a type/member

Robert Levy
  • 28,747
  • 6
  • 62
  • 94
2

While it is possible for you to specify the same Attribute class multiple times, it may be more beneficial to define each property as a separate, independent attribute.

For example VisibilityAttribute, MyAttributeAttribute, ... etc. You could then apply each attribute individually to properties.

If it seems cumbersome to reflect each manually, you could make a helper method that would reflect all the possible ones and return back a Dictionary object with each one's key/value.

For example:

[Visibility("true")]
[MyAttribute("Something")]

Then you could have a helper method that does something like this:

static Dictionary<string, string> GetAttributeDictionary(object value)
{
  var dictionary = new Dictionary<string, string>();

  var type = value.GetType();
  var customAttributes = type.GetCustomAttributes(true);

  foreach (var attribute in customAttributes)
  {
    if (attribute is VisibilityAttribute)
    {
       var visibilityAttribute = attribute as VisibilityAttribute;
       dictionary["Visibility"] = visibilityAttribute.Visibility;
    }

    // Process other custom attributes...
  }

  return dictionary;
}
Erik
  • 12,730
  • 5
  • 36
  • 42
  • I don't know what the attribute keys will be in advance because they are developer defined. However, this gave me another idea - I can use an interface to determine which types of attributes I am interested in and those types that define the interface can be named whatever the end developer wants. And the name of the type (minus the suffix Attribute) could be used as the key in the resultant dictionary. It's still not pretty because I have to break up my one custom attribute into 2, but at least there is compile-time checking and intellisense if I go this route. – NightOwl888 Jan 21 '14 at 18:19
  • Great but this means the dictionary needs to be recreated from attributes each time they are needed. – ed22 Mar 27 '22 at 10:42
0

You cant send dictionary type with others parameters, but you can add dictionary object at constructor, like this: (realisation from my project)

public class Class1
{
    [SettingsButton("Test", "Select")]
    public string Select { get; set; } = "1";
}

[AttributeUsage(AttributeTargets.Property)]
class SettingsButtonAttribute : Attribute
{
    public string Name { get; }
    public Dictionary<string, object>? Selectable { get; set; } = null;
    public SettingsButtonAttribute(string name) => Name = name;
    public SettingsButtonAttribute(string name, string dict) => (Name, Selectable) = (name, Global[dict]);

    public static Dictionary<string, Dictionary<string, object>> Global = new()
    {
        {"Select", new()
        {
            {"first", "1" },
            {"second", "2" }
        }
        }

    };

}