简体   繁体   English

如何声明值为字符串数组的 DefaultValue 属性?

[英]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.我一直在代码生成器中使用 DefaultValue 属性,它从架构中编写 C# 类定义。

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#:我想在我的 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[] .当你想传递一个新的字符串数组时,你必须实例化它——这是由new string[] C# allows an initialization list with the initial contents of the array to follow in braces, ie { "a", "b" } . C# 允许带有数组初始内容的初始化列表紧跟在大括号中,即{ "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.编辑:正如Cory-G正确指出的那样,您可能希望确保您的实际实例不会收到存储在DefaultValue属性中的数组实例。 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.相反,您可以使用属性的 setter 来复制分配的数组。

Another solution is to take advantage of the virtual nature of DefaultValueAttribute and derive your own.另一种解决方案是利用DefaultValueAttribute的虚拟性质并派生自己的。 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 .定义自己的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.但是,如果您将它与 Newtonsoft Json 或主要仅用于比较值的东西一起使用,那么您可能希望其中的值更加恒定,以节省时间而不是每次都重新创建对象。

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:其中属性DefaultValueStatic定义为:

  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.这对于 Newtonsoft json 之类的东西很有用,其中 setter 不会经常被调用,但默认比较会。

Again, just make sure you know a bit of how the attribute will be used.同样,只需确保您对如何使用该属性有所了解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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