2012-07-10 13 views
5

W ostatnim czasie pojawił się w C# problem dość kłopotliwy. W naszej bazie kodu mamy klasę TreeNode. Podczas zmiany kodu okazało się, że nie można przypisać zmiennej do właściwości Nodes. Po bliższej inspekcji stało się jasne, że właściwość jest tylko do odczytu i można się spodziewać takiego zachowania.Ustawianie właściwości tylko do odczytu z anonimowym typem

Dziwne jest to, że nasza podstawa kodu do tej pory zawsze polegała na przypisaniu jakiegoś anonimowego typu do właściwości Nodes i została skompilowana i działała idealnie.

Podsumowując: dlaczego zlecenie w AddSomeNodes działało w pierwszej kolejności?

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 
      }; 
     } 
    } 
} 

Odpowiedz

4

AddSomeNodes nie tworzy egzemplarz List<TreeNode> ponieważ składni jest inicjator Collection (a więc nie jest przypisanie Nodes rozumieniu nie łamie to umowy readonly), kompilator faktycznie tłumaczy inicjator kolekcji na połączenia na .Add.

W rzeczywistości połączenie AddSomeOtherNodes próbuje ponownie przypisać wartość, ale jest to readonly. Jest to również składnia inicjatora obiektu, która tłumaczy się na proste wywołania właściwości. Ta właściwość nie ma narzędzia ustawiającego, więc wywołanie generuje błąd kompilatora. Próba dodania ustawiającego, który ustawia wartość tylko do odczytu, spowoduje wygenerowanie kolejnego błędu kompilatora, ponieważ jest oznaczony jako readonly.

From MSDN:

Stosując inicjator zbiórki nie trzeba podać wiele wywołań metody Add klasy w kodzie źródłowym; kompilator dodaje połączenia.

Ponadto, tylko w celu wyjaśnienia, istnieją żadnych anonimowe typy w sztuce w kodzie - wszystko to jest składnia inicjator.


Nie ma związku z pytaniem, ale znajduje się w tym samym obszarze.

Co ciekawe, składnia Nodes = { new TreeNode() } nie działa z lokalnego członka, wydaje się tylko do pracy, gdy jest zagnieżdżona wewnątrz inicjatora obiektu lub podczas przypisywania obiektu:

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 } 
}; 

dokumentacji MSDN nie wydaje aby uzyskać jakiekolwiek wyjaśnienia na ten temat.

+0

@Downvoter Dlaczego upadł? Moja obserwacja sytuacji w OP jest dokładna zgodnie z dokumentacją i własnymi testami. Nie wspominając o wszystkich innych odpowiedziach. –

+0

Właśnie dostałem głosowanie w dół! – MBen

+0

Dzięki za tak szybką i kompletną odpowiedź! Masz całkowitą rację, że popełniłem błąd, nazywając to anonimowym typem. Takie zachowanie ma sens, gdy bierze się pod uwagę składnię inicjalizacji kolekcji, a nie przypisanie typu anonimowego. Czasami trudno jest ujednoznacznić te koncepcje psychicznie, ponieważ są one tak podobne pod względem składniowym. Dzięki! – Michiel

1

nie przypisuje jest dodanie elementu do ICollection

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

Własność węzłów nie jest przypisana.

Używając specjalnej inicjator kolekcji:

CollectionProperty = { a, b, c }; 

zostaje zmieniony na:

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

Kompilator równowartość co się stało to:

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

    yield return node; 
} 

Ważnym wyróżnieniem jest to, że użyli składni inicjatora kolekcji , aby przypisać wartość.

1

skompilowany kod (po usunięciu metodą AddSomeOtherNodes) i otworzył ją w reflektor i oto wynik:

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

Jak widać z tej składni metoda dodawania nazywany jest na zmiennej węzłów .

Powiązane problemy