5

つい最近、C# で不可解なことが起こりました。コードベースにはTreeNodeクラスがあります。Nodes一部のコードを変更したときに、プロパティに変数を割り当てることができないことがわかりました。よく調べてみると、プロパティが読み取り専用であり、この動作が想定されていることが明らかになりました。

奇妙なことに、私たちのコード ベースはそれまで常に何らかの匿名型のNodesプロパティへの割り当てに依存しており、コンパイルして完全に機能していました。

要約する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
             };
        }
    }
}
4

5 に答える 5

4

AddSomeNodesその構文はコレクション初期化子であるためのインスタンスを作成していません(したがって、契約を破らないことを意味するにList<TreeNode>割り当てていません)、コンパイラは実際にコレクション初期化子を への呼び出しに変換します。Nodesreadonly.Add

呼び出しはAddSomeOtherNodes実際に値を再割り当てしようとしますが、それはreadonly. これはオブジェクト初期化構文でもあり、単純なプロパティ呼び出しに変換されます。このプロパティにはセッターがないため、呼び出しによってコンパイラ エラーが生成されます。readonly 値を設定するセッターを追加しようとすると、別のコンパイラ エラーが生成されreadonlyます。

MSDN から:

コレクション初期化子を使用すると、ソース コードでクラスの Add メソッドへの複数の呼び出しを指定する必要がなくなります。コンパイラが呼び出しを追加します。

また、明確にするために、コードで使用されている匿名型はありません-それはすべて初期化構文です。


あなたの質問とは関係ありませんが、同じエリアにあります。

興味深いことに、このNodes = { 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 }
};

MSDN のドキュメントには、これに関する明確な説明がないようです。

于 2012-07-10T14:15:50.047 に答える
1

コードをコンパイルして (AddSomeOtherNodes メソッドを削除した後)、Reflector で開いた結果は次のとおりです。

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

ご覧のとおり、この構文では Add メソッドが Nodes 変数で呼び出されます。

于 2012-07-10T14:19:20.510 に答える
1

これは代入ではなく、要素をICollection

 Nodes = {new TreeNode() }, that is why it works.
于 2012-07-10T14:15:46.590 に答える