11

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?

8
  • ["a","b"] is not valid syntax. new[] { "a", "b" } is. Commented Sep 18, 2012 at 14:19
  • @Jon: In fact, new[] { "a", "b" } is not valid C# syntax either, or is it? I mean, without an element type of the array? Commented Sep 18, 2012 at 14:22
  • 1
    @O.R.Mapper: Implicitly typed arrays. Commented Sep 18, 2012 at 14:23
  • @Jon: Interesting new feature :-) We aren't yet using .NET 4.5 (in fact, most of our projects are still stuck in the 3.5 toolset), so I hadn't yet encountered this. Commented Sep 18, 2012 at 14:28
  • @O.R.Mapper: Not quite new, it's been there since C# 3.0. Commented Sep 18, 2012 at 14:32

2 Answers 2

13

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, i.e. { "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.

Sign up to request clarification or add additional context in comments.

7 Comments

@MichaelBishop: Your comment on the question implies you are using System.ComponentModel.DefaultValueAttribute? (That wasn't clear in the question; from the question, I assumed it's some other thusly named attribute type that offers a (Type, object) constructor.) If so, you could try and use this constructor and not pass any Type.
[DefaultValue(new string[] { "a", "b" })] This works. Thank you! I saw all the DefaultValueAttribute constructors for specific types and didn't even realize I could pass a generic object.
How would you do this with generic list? Like this as default value new List<CommonModels.Contact>
I come across this answer while looking for a more generic syntax. This does not appear to work with custom class/struct definitions e.g. [DefaultValue(new Whatever[] {})] (I wanted empty array default)
This solution seems potentially dangerous for the unaware. Pretty sure this will return a shared reference, meaning that if you alter the array at all, then the default held by the attribute along with all that got this value will be changed. If using this, maybe put a bit of "copy array" code in your setter or something.
|
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.