简体   繁体   中英

How do I declare a DefaultValue attribute whose value is an array of strings?

I've been using the DefaultValue attribute in a code generator which writes a C# class definition from a schema.

I'm stuck where a property in the schema is an array of strings.

I'd like to write something like this in my C#:

[DefaultValue(typeof(string[]), ["a","b"])]
public string[] Names{get;set;}

but that won't compile.

Is there any way I can successfully declare the default value attribute for a string array?

You could try

[DefaultValue(new string[] { "a", "b" })]

As you want to pass a new string array, you have to instantiate it - that's done by new string[] . C# allows an initialization list with the initial contents of the array to follow in braces, ie { "a", "b" } .


EDIT: As correctly pointed out by Cory-G , you may want to make sure your actual instances do not receive the very array instance stored in the DefaultValue attribute. Otherwise, changes to that instance might influence default values across your application.

Instead, you could use your property's setter to copy the assigned array.

Another solution is to take advantage of the virtual nature of DefaultValueAttribute and derive your own. Below are some examples with non-constant-able types.

Example Usage:

public class Foo
{
    [DefaultValueNew( typeof(List<int>), new int[]{2,2} )]
    public List<int> SomeList { get; set; }

    // -- Or --
    public static List<int> GetDefaultSomeList2() => new List<int>{2,2};

    [DefaultValueCallStatic( typeof(Foo), nameof(GetDefaultSomeList2) )]
    public List<int> SomeList2 { get; set; }
};

Here are the definitions of those attributes:

  public class DefaultValueNewAttribute : DefaultValueAttribute
  {
    public Type Type { get; }
    public object[] Args { get; }

    public DefaultValueNewAttribute(Type type, params object[] args)
      : base(null)
    {
      Type = type;
      Args = args;
    }

    public override object? Value => Activator.CreateInstance(Type, Args);
  };

  public class DefaultValueCallStaticAttribute : DefaultValueAttribute
  {
    public Type Type { get; }
    public string Method { get; }

    public DefaultValueCallStaticAttribute(Type type, string method)
      : base(null)
    {
      Type = type;
      Method = method;
    }

    public override object? Value => Type.GetMethod(Method, BindingFlags.Public | BindingFlags.Static).Invoke(null, null);
  };

Gotchas to be aware of

Be careful when defining your own DefaultValueAttribute . Most importantly, learn a bit about how it will get used before creating it. For something like a code generator, the above is likely ok. If, however, you are using it with Newtonsoft Json or something that will mostly only use it to compare values, then you may want the value within to be more constant, in order to save time and not re-create the object every time.

For a situation where you want the value to not be re-created every time, you may do something more like:

  public static readonly List<int> DefaultAges = new List<int>{2,2};
  private List<int> __ages = new List<int>(DefaultAges);
  [DefaultValueStatic( typeof(List<int>), nameof(DefaultAges) )]
  public List<int> Ages { get => __ages; set {__ages = new List<int>(value);}

Where the attribute DefaultValueStatic is defined as:

  public class DefaultValueStaticAttribute : DefaultValueAttribute
  {
    public DefaultValueStaticAttribute(Type type, string memberName) : base(null)
    {
      foreach (var member in type.GetMember( memberName, BindingFlags.Static | BindingFlags.Public ))
      {
        if (member is FieldInfo fi) { SetValue(fi.GetValue(type)); return; }
        if (member is PropertyInfo pi) { SetValue(pi.GetValue(type)); return; }
      }
      throw new ArgumentException($"Unable to get static member '{memberName}' from type '{type.Name}'");
    }
  };

The above version will ensure that the default value is not re-created. This can be useful with something like Newtonsoft json, where the setters won't be called often, but the default comparison will.

Again, just make sure you know a bit of how the attribute will be used.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM