7

C# 中的属性可以与集合初始化程序一起使用吗?

例如,我想做如下的事情:

[DictionaryAttribute(){{"Key", "Value"}, {"Key", "Value"}}]
public class Foo { ... }

我知道属性可以具有命名参数,并且因为这看起来与对象初始化器非常相似,所以我想知道集合初始化器是否也可用。

4

3 回答 3

6

C# 4.0 规范的第 17.1.3 节明确不允许在属性参数中使用多维数组,因此虽然 Foo(string[,] bar) 可能允许您调用 Foo(new [,] {{"a", "b "}, {"key2", "val2"}}),不幸的是,它不适用于属性。

因此,考虑到这一点,近似于您想要的一些可能性是:

  1. 使用具有交替键和值对的一维数组。这种方法的明显缺点是它不完全强制执行名称和值。

  2. 通过使用以下属性标记属性定义,允许您的参数多次出现:

    [AttributeUsage(AllowMultiple=true)]
    

    这样,您现在可以定义:

    [KeyVal("key1","val1"), KeyVal("key2","val2")]
    public class Foo { ... }
    

    这比我确定您所希望的要冗长一些,但它清楚地划分了名称和值。

  3. 查找 JSON 包并为您的属性提供初始化程序。性能损失无关紧要,因为这是在代码初始化期间完成的。例如,使用 Newtonsoft.Json,您可以创建如下属性:

        public class JsonAttribute : Attribute
        {
          Dictionary<string, string> _nameValues = 
              new Dictionary<string, string>();
          public JsonAttribute(string jsoninit)
          {
            var dictionary = new Dictionary<string, string>();
            dynamic obj = JsonConvert.DeserializeObject(jsoninit);
            foreach(var item in obj)
              _nameValues[item.Name] = item.Value.Value;
          }
        }
    

    这将允许您像这样实例化一个属性:

    [Json(@"{""key1"":""val1"", ""key2"":""val2""}")]
    public class Foo { ... }
    

    我知道这有点令人高兴,更多的参与,但你就是这样。无论如何,在这个疯狂的动态世界中,知道如何使用 JSON 初始化对象并不是一项糟糕的技能。

于 2011-08-03T21:26:00.840 回答
6

更新:对不起,我弄错了 - 传递自定义类型的数组是不可能的:(

属性类的位置参数和命名参数的类型仅限于属性参数类型,它们是:

  1. 以下类型之一:bool、byte、char、double、float、int、long、short、string
  2. 类型对象。
  3. 类型 System.Type。
  4. 一个 Enum 类型,前提是它具有公共可访问性,并且它嵌套的类型(如果有)也具有公共可访问性(第 17.2 节)。
  5. 上述类型 的一维数组。资源

资料来源:堆栈溢出

您可以声明传递自定义类型的数组:

class TestType
{
  public int Id { get; set; }
  public string Value { get; set; }

  public TestType(int id, string value)
  {
    Id = id;
    Value = value;
  }
}

class TestAttribute : Attribute
{
  public TestAttribute(params TestType[] array)
  {
    //
  }
}

但在属性声明时出现编译错误:

[Test(new[]{new TestType(1, "1"), new TestType(2, "2"), })]
public void Test()
{

}
于 2011-08-03T20:27:42.590 回答
3

最简洁的答案是不。

更长的答案:为了让一个类支持集合初始化器,它需要实现 IEnumerable 并且它需要有一个 add 方法。例如:

public class MyClass<T,U> : IEnumerable<T>
{
    public void Add(T t, U u)
    {
    }

    public IEnumerator<T> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

然后我可以这样做:

var mc = new MyClass<int, string> {{1, ""}, {2, ""}};

因此,使用它,让我们尝试使其适用于属性。(旁注,由于属性不支持泛型,我只是使用字符串对其进行硬编码以进行测试):

public class CollectionInitAttribute : Attribute, IEnumerable<string>
{
    public void Add(string s1, string s2)
    {

    }

    public IEnumerator<string> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

现在来测试它:

[CollectionInit{{"1","1"}}]
public class MyClass
{

}

并且不编译:(我不确定限制到底在哪里,我猜属性没有像常规对象那样更新,因此不支持。我很好奇是否理论上,该语言的未来版本可以支持这一点....

于 2011-08-03T20:26:10.440 回答