使用匿名类型设置只读属性

时间:2022-09-12 08:48:09

I came about something rather baffling in C# just recently. In our code base, we have a TreeNode class. When changing some code, I found that it was impossible to assign a variable to the Nodes property. On closer inspection it became clear that the property is read-only and this behavior is to be expected.

最近我在c#中遇到了一些令人困惑的事情。在我们的代码库中,我们有一个TreeNode类。在更改某些代码时,我发现将一个变量赋给节点属性是不可能的。仔细检查之后,很明显,该属性是只读的,这种行为是可以预期的。

What is strange is that our code base had until then always relied on assignment of some anonymous type to the Nodes property and compiled and worked perfectly.

奇怪的是,在此之前,我们的代码库一直依赖于将某些匿名类型赋值给节点属性,并进行了完美的编译和工作。

To summarize: why did the assignment in AddSomeNodes work in the first place?

综上所述:为什么在AddSomeNodes中的作业一开始就起作用?

using System.Collections.Generic;

namespace ReadOnlyProperty
{
    public class TreeNode
    {
        private readonly IList<TreeNode> _nodes = new List<TreeNode>();

        public IList<TreeNode> Nodes
        {
            get { return _nodes;  }
        }
    }

    public class TreeBuilder
    {
        public IEnumerable<TreeNode> AddSomeNodes()
        {
            yield return new TreeNode
             {
                Nodes = { new TreeNode() }
             };
        }

        public IEnumerable<TreeNode> AddSomeOtherNodes()
        {
            var someNodes = new List<TreeNode>();

            yield return new TreeNode
             {
                Nodes = someNodes
             };
        }
    }
}

5 个解决方案

#1


4  

AddSomeNodes is not creating an instance of List<TreeNode> because that syntax is a collection initializer (therefore it is not assigning to Nodes meaning it doesn't break the readonly contract), the compiler actually translates the collection initializer into calls to .Add.

AddSomeNodes并没有创建List 的实例,因为该语法是一个集合初始化器(因此它没有分配给节点,这意味着它不会破坏readonly契约),编译器实际上会将集合初始化器转换为对. add的调用。

The AddSomeOtherNodes call actually tries to re-assign the value, but it is readonly. This is also the object initializer syntax, which translates into simple property calls. This property does not have a setter, so that call generates a compiler error. Attempting to add a setter that sets the readonly value will generate another compiler error because it is marked readonly.

AddSomeOtherNodes调用实际上试图重新分配值,但它是只读的。这也是对象初始化语法,它转换为简单的属性调用。此属性没有setter,因此调用将生成编译器错误。尝试添加设置readonly值的setter将生成另一个编译器错误,因为它被标记为readonly。

From MSDN:

从MSDN:

By using a collection initializer you do not have to specify multiple calls to the Add method of the class in your source code; the compiler adds the calls.

通过使用集合初始化器,您不必在源代码中指定对类的Add方法的多个调用;编译器添加调用。

Also, just to clarify, there are no anonymous types in play in your code - it is all initializer syntax.

另外,澄清一下,在您的代码中没有匿名类型——它都是初始化器语法。


Unrelated to your question, but in the same area.

Interestingly, the Nodes = { new TreeNode() } syntax doesn't work with a local member, it only seems to work when it is nested inside an object initializer or during object assignment:

有趣的是,node = {new TreeNode()}语法对本地成员不起作用,似乎只在对象初始化器内嵌套或对象分配期间起作用:

List<int> numbers = { 1, 2, 3, 4 }; // This isn't valid.
List<int> numbers = new List<int> { 1, 2, 3, 4 }; // Valid.

// This is valid, but will NullReferenceException on Numbers
// if NumberContainer doesn't "new" the list internally.
var container = new NumberContainer()  
{
    Numbers = { 1, 2, 3, 4 }
};

The MSDN documentation doesn't seem to have any clarification on this.

MSDN文档似乎没有对此做任何说明。

#2


2  

Your nodes property is not being assigned.

您的节点属性没有被分配。

Using the special collection initializer:

使用特殊集合初始化器:

CollectionProperty = { a, b, c };

Is changed to:

更改为:

CollectionProperty.Add(a);
CollectionProperty.Add(b);
CollectionProperty.Add(c);

#3


1  

This is not assigning it is adding an element to the ICollection

这并不是赋值,而是向ICollection添加元素

 Nodes = {new TreeNode() }, that is why it works.

#4


1  

I compiled your code (after removing the AddSomeOtherNodes method) and opened it in Reflector and here is the result:

我编译了您的代码(删除了addsomeothernode方法之后),然后在Reflector中打开它,结果如下:

public IEnumerable<TreeNode> AddSomeNodes()
{
    TreeNode iteratorVariable0 = new TreeNode();
    iteratorVariable0.Nodes.Add(new TreeNode());
    yield return iteratorVariable0;
}

As you can see, with this syntax the Add method is called on the Nodes variable.

正如您所看到的,使用这种语法,在节点变量上调用Add方法。

#5


0  

The compiler equivalent to what happened is this:

与所发生的事情相对应的编译器是这样的:

public IEnumerable<TreeNode> AddSomeNodes()
{
    TreeNode node = new TreeNode();
    node.Nodes.Add(new TreeNode());

    yield return node;
}

The important distinction here is that they used the collection initializer syntax to assign the value.

这里的重要区别是,它们使用集合初始化语法来分配值。

#1


4  

AddSomeNodes is not creating an instance of List<TreeNode> because that syntax is a collection initializer (therefore it is not assigning to Nodes meaning it doesn't break the readonly contract), the compiler actually translates the collection initializer into calls to .Add.

AddSomeNodes并没有创建List 的实例,因为该语法是一个集合初始化器(因此它没有分配给节点,这意味着它不会破坏readonly契约),编译器实际上会将集合初始化器转换为对. add的调用。

The AddSomeOtherNodes call actually tries to re-assign the value, but it is readonly. This is also the object initializer syntax, which translates into simple property calls. This property does not have a setter, so that call generates a compiler error. Attempting to add a setter that sets the readonly value will generate another compiler error because it is marked readonly.

AddSomeOtherNodes调用实际上试图重新分配值,但它是只读的。这也是对象初始化语法,它转换为简单的属性调用。此属性没有setter,因此调用将生成编译器错误。尝试添加设置readonly值的setter将生成另一个编译器错误,因为它被标记为readonly。

From MSDN:

从MSDN:

By using a collection initializer you do not have to specify multiple calls to the Add method of the class in your source code; the compiler adds the calls.

通过使用集合初始化器,您不必在源代码中指定对类的Add方法的多个调用;编译器添加调用。

Also, just to clarify, there are no anonymous types in play in your code - it is all initializer syntax.

另外,澄清一下,在您的代码中没有匿名类型——它都是初始化器语法。


Unrelated to your question, but in the same area.

Interestingly, the Nodes = { new TreeNode() } syntax doesn't work with a local member, it only seems to work when it is nested inside an object initializer or during object assignment:

有趣的是,node = {new TreeNode()}语法对本地成员不起作用,似乎只在对象初始化器内嵌套或对象分配期间起作用:

List<int> numbers = { 1, 2, 3, 4 }; // This isn't valid.
List<int> numbers = new List<int> { 1, 2, 3, 4 }; // Valid.

// This is valid, but will NullReferenceException on Numbers
// if NumberContainer doesn't "new" the list internally.
var container = new NumberContainer()  
{
    Numbers = { 1, 2, 3, 4 }
};

The MSDN documentation doesn't seem to have any clarification on this.

MSDN文档似乎没有对此做任何说明。

#2


2  

Your nodes property is not being assigned.

您的节点属性没有被分配。

Using the special collection initializer:

使用特殊集合初始化器:

CollectionProperty = { a, b, c };

Is changed to:

更改为:

CollectionProperty.Add(a);
CollectionProperty.Add(b);
CollectionProperty.Add(c);

#3


1  

This is not assigning it is adding an element to the ICollection

这并不是赋值,而是向ICollection添加元素

 Nodes = {new TreeNode() }, that is why it works.

#4


1  

I compiled your code (after removing the AddSomeOtherNodes method) and opened it in Reflector and here is the result:

我编译了您的代码(删除了addsomeothernode方法之后),然后在Reflector中打开它,结果如下:

public IEnumerable<TreeNode> AddSomeNodes()
{
    TreeNode iteratorVariable0 = new TreeNode();
    iteratorVariable0.Nodes.Add(new TreeNode());
    yield return iteratorVariable0;
}

As you can see, with this syntax the Add method is called on the Nodes variable.

正如您所看到的,使用这种语法,在节点变量上调用Add方法。

#5


0  

The compiler equivalent to what happened is this:

与所发生的事情相对应的编译器是这样的:

public IEnumerable<TreeNode> AddSomeNodes()
{
    TreeNode node = new TreeNode();
    node.Nodes.Add(new TreeNode());

    yield return node;
}

The important distinction here is that they used the collection initializer syntax to assign the value.

这里的重要区别是,它们使用集合初始化语法来分配值。