Add to a readonly collection in a constructor? - c#

Is there a c# language construct that will allow me to add items to a readonly collection property in a constructor? I want to do something like this:
public class Node{
public IList<Node> Children {get; protected set;}
public Node(){
Children = new ObservableList<Node>();
}
}
... in my code somewhere...
var node = new Node {Children.Add(new Node())};
(not a great example, but I hope this gets the idea across)...
UPDATE
OK sorry I need to be clearer. I didn't write this Node class, and I cannot change it. I am asking if there is a c# language concept that will allow me to add to the readonly collection in the parameterless constructor in the second snippet, assuming the Node class is not changeable.

Try this. It is definitely possible to add elements on construction
var node = new Node
{
Children =
{
new Node(),
new Node()
}
};

If you have a property of type List that is get only, that only means you can't set that property, you can still add things to the list.
You could however expose an IEnumerable property instead and have a constructor that takes a list(or another IEnumerable more likely).
Property initializers do not work since the compiler will just rewrite them to regular property assignments.
I'd do this:
public class Node{
public IEnumerable<Node> Children {get; private set;}
public Node(IEnumerable<Node> children){
Children = children.ToList();
}
}
if you can't change the Node class, I suggest writing a helper class similar to this:
public static Node Create(IEnumerable<Node> children)
{
var n = new Node();
foreach (var c in children)
n.Children.Add(c);
return n;
}

To use the collection initializer syntax from you second code snippet your Node class must implement IEnumerable and have a public method with the signature
void Add(Node child)
Hence such a class cannot offer the immutability you desire. I think the best solution to your problem would be to do this
public class Node
{
public readonly IEnumerable<Node> Children;
public Node(IEnumerable<Node> children)
{
Children = children;
}
}
or if you do not like the deferred execution of IEnumerable:
public class Node
{
public readonly ReadOnlyCollection<Node> Children;
public Node(IEnumerable<Node> children)
{
Children = new ReadOnlyCollection<Node>(children);
}
}

You can add a backing field to the "Children" property, then just populate the backing field during construction.
Like so
public class Node
{
private IList<Node> _Children;
public IList<Node> Children { get { return _Children; } }
public Node(IList<Node> children)
{
_Children = children;
}
}
Then you can do this
var node = new Node((new ObservableList<Node>()).Add(new Node()));

Related

How to use custom Add method for collection deserialization process?

I have some class with tree node structure. It has Children property with read only collection type for hide direct changing of children and AddChild(...) method for control children adding.
class TreeNode {
List<TreeNode> _children = new List<TreeNode>();
public IReadOnlyList<TreeNode> Children => children;
public string Name { get; set; } // some other filed
public void AddChild(TreeNode node){
// ... some code
_children.Add(node);
}
}
And I need to provide deserialization for my class. I tried:
[Serializable]
[XmlRoot(ElementName = "node")]
class TreeNode {
List<TreeNode> _children = new List<TreeNode>();
[XmlElement(ElementName = "node")]
public IReadOnlyList<TreeNode> Children => children;
[XmlAttribute(DataType = "string", AttributeName = "name")]
public string Name { get; set; } // some other filed
public void AddChild(TreeNode node){
// ... some code
_children.Add(node);
}
public static TreeNode Deserialize(Stream stream) {
var serializer = new XmlSerializer(typeof(TreeNode));
var obj = serializer.Deserialize(stream);
var tree = (TreeNode)obj;
return tree;
}
}
Of course, this doesn't work because IReadOnlyList has no Add method.
Is it possible to bind AddChild to deserialization process? And if 'yes' - How?
How to provide the same encapsulation level with deserialization ability?
If this is XmlSerializer, then: no, you can't do that unless you implement IXmlSerializable completely, which is very hard to do correctly, and defeats the entire purpose of using XmlSerializer in the first place.
If the data isn't huge, then my default answer to any problem of the form "my existing object model doesn't work well with my chosen serializer" is: when it gets messy, stop serializing your existing object model. Instead, create a separate DTO model that is designed solely to work well with your chosen serializer, and map the data into the DTO model before serialization - and back again afterwards. This might mean using List<T> in the DTO model rather than IReadOnlyList<T>.
This can be done by adding a surrogate property to TreeNode that returns a surrogate wrapper type that implements both IEnumerable<T> and Add(T) using delegates provided to its constructor. First, introduce the following surrogate wrapper:
// Proxy class for any enumerable with the requisite `Add` methods.
public class EnumerableProxy<T> : IEnumerable<T>
{
readonly Action<T> add;
readonly Func<IEnumerable<T>> getEnumerable;
// XmlSerializer required default constructor (which can be private).
EnumerableProxy()
{
throw new NotImplementedException("The parameterless constructor should never be called directly");
}
public EnumerableProxy(Func<IEnumerable<T>> getEnumerable, Action<T> add)
{
if (getEnumerable == null || add == null)
throw new ArgumentNullException();
this.getEnumerable = getEnumerable;
this.add = add;
}
public void Add(T obj)
{
// Required Add() method as documented here:
// https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer%28v=vs.100%29.aspx
add(obj);
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return (getEnumerable() ?? Enumerable.Empty<T>()).GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
Next, modify your TreeNode by marking Children with [XmlIgnore] and adding a surrogate property that returns a pre-allocated EnumerableProxy<TreeNode>:
[XmlRoot(ElementName = "node")]
public class TreeNode
{
List<TreeNode> _children = new List<TreeNode>();
[XmlIgnore]
public IReadOnlyList<TreeNode> Children { get { return _children; } }
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
[XmlElement(ElementName = "node")]
public EnumerableProxy<TreeNode> ChildrenSurrogate
{
get
{
return new EnumerableProxy<TreeNode>(() => _children, n => AddChild(n));
}
}
[XmlAttribute(DataType = "string", AttributeName = "name")]
public string Name { get; set; } // some other filed
public void AddChild(TreeNode node)
{
// ... some code
_children.Add(node);
}
}
Your type can now be fully serialized and deserialized by XmlSerializer. Working .NET fiddle.
This solution takes advantage of the following documented behaviors of XmlSerializer. Firstly, as stated in Remarks for XmlSerializer:
The XmlSerializer gives special treatment to classes that implement IEnumerable or ICollection. A class that implements IEnumerable must implement a public Add method that takes a single parameter. The Add method's parameter must be of the same type as is returned from the Current property on the value returned from GetEnumerator, or one of that type's bases.
Thus your surrogate IEnumerable<T> wrapper does not actually need to implement ICollection<T> with its full set of methods including Clear(), Remove(), Contains() and so on. Just an Add() with the correct signature is sufficient. (If you wanted to implement a similar solution for, say, Json.NET, your surrogate type would need to implement ICollection<T> - but you could just throw exceptions from the unnecessary methods such as Remove() and Clear().)
Secondly, as stated in Introducing XML Serialization:
XML serialization does not convert methods, indexers, private fields, or read-only properties (except read-only collections).
I.e. XmlSerializer can successfully deserialize the items in a pre-allocated collection even when that collection is returned by a get-only property. This avoids the need to implement a set method for the surrogate property or a default constructor for the surrogate collection wrapper type.

Inner class generic type same as outer type

I found a question here that almost answers my question, but I still don't fully understand.
Trying to write a Tree data structure, I did this:
public class Tree<T>
{
public TreeNode<T> root;
...
public class TreeNode<T>
{
List<TreeNode<T>> children;
T data;
public T Data { get { return data; } }
public TreeNode<T>(T data)
{
this.data = data;
children = new List<TreeNode<T>>();
}
...
}
}
And, anyone who's worked with C# generics apparently knows that I got this compiler warning: Type parameter 'T' has the same name as the type parameter from outer type 'Tree<T>'
My intent was to create an inner class that would be forced to use the same type as the outer class, but I now understand that adding a type parameter actually allows the inner class to be more flexible. But, in my case, I want subclasses of Tree<T> to be able to use TreeNode, for example, like this:
public class IntTree : Tree<int>
{
...
private static IntTree fromNode(TreeNode<int> node)
{
IntTree t = new IntTree();
t.root = node;
return t;
}
}
(That method allows the subclass to implement ToString() recursively)
So my question is, if I take out the parameterization, like this:
public class Tree<T>
{
public TreeNode root;
...
public class TreeNode
{
List<TreeNode> children;
T data;
public T Data { get { return data; } }
public TreeNode(T data)
{
this.data = data;
children = new List<TreeNode>();
}
...
}
}
will the resulting subclass be forced to use integers when creating TreeNodes, and therefore never be able to break the intent I had?
Disclaimer: yes, I know I'm probably doing plenty of things wrong here. I'm still learning C#, coming from a mostly Java and Lisp background, with a little bit of plain C. So suggestions and explanations are welcome.
Yes, it will be forced to use the same type. Look at the declaration again:
public class Tree<T>
{
public class TreeNode
{
private T Data;
}
}
So the type of Data is determined when you instantiate a specific Tree:
var tree = new Tree<int>();
This way the type of Data is declared as int and can be no different.
Note that there is no non-generic TreeNode class. There is only a Tree<int>.TreeNode type:
Tree<int> intTree = new Tree<int>(); // add some nodes
Tree<int>.TreeNode intNode = intTree.Nodes[0]; // for example
Tree<string> stringTree = new Tree<int>(); // add some nodes
Tree<string>.TreeNode stringNode = stringTree.Nodes[0]; // for example
// ERROR: this won't compile as the types are incompatible
Tree<string>.TreeNode stringNode2 = intTree.Nodes[0];
A Tree<string>.TreeNode is a different type than Tree<int>.TreeNode.
The type T declared in the outer class may already be used in all its inner declarations, so you can simply remove the <T> from the inner class:
public class Tree<T>
{
public TreeNode root;
//...
public class TreeNode
{
List<TreeNode> children;
T data;
public T Data { get { return data; } }
public TreeNode(T data)
{
this.data = data;
children = new List<TreeNode>();
}
//...
}
}

How to update an entire object in a list C#?

I have a list of object and I want to replace one of the objects in the list with the new object:
public Parent AppendParentChildren(Request request)
{
var Children = request.Parent.Children.ToList();
if (Children.Any(x => x.TrackingNumber == request.Child.TrackingNumber))
{
//Here I want to replace any Children that have the same tracking number in the list with the new Child passed in
}
else
{
Children.Add(request.Child);
}
request.Parent.Children = Children;
return request.Parent;
}
public class Request
{
public Parent Parent { get; set; }
public Child Child { get; set; }
}
public class Parent
{
public IEnumerable<Child> Children {get;set;}
}
If I try and use it in a loop:
public static class Extension
{
public static void Update<T>(this List<T> items, T newItem)
{
foreach (var item in items)
{
//this
item = newItem;
}
}
}
item is read only, so I cannot replace the object in the list.
Any suggestions?
You can't change the member of a foreach iteration because foreach implements the IEnumerable type which is read-only.
A solution would be to cast the list of items inside the extension method as a List (which is read-writable). Then you'd want to identify which item(s) in the list you are replacing and update them. Below is what the Update extension method would look like (assuming you're in a situation where you can use LINQ)
public static class Extension
{
public static void Update<T>(this List<T> items, T newItem)
{
var childList = items as List<Child>;
var newChildItem = newItem as Child;
var matches = childList.Where(x => x.TrackingNumber == newChildItem.TrackingNumber).ToList();
matches.ForEach(x => childList[childList.IndexOf(x)] = newChildItem);
}
}
I've put a working example (albeit slightly bloated) on dotnetfiddle
https://dotnetfiddle.net/MJ5svP
It's also worth noting that although it looks like you're altering childList, this is actually referenced back to the original list not creating a copy (more info on this here)

C# xml serializer - serialize derived objects

I want to serialize the following:
[Serializable]
[DefaultPropertyAttribute("Name")]
[XmlInclude(typeof(ItemInfo))]
[XmlInclude(typeof(ItemInfoA))]
[XmlInclude(typeof(ItemInfoB))]
public class ItemInfo
{
public string name;
[XmlArray("Items"), XmlArrayItem(typeof(ItemInfo))]
public ArrayList arr;
public ItemInfo parentItemInfo;
}
[Serializable]
[XmlInclude(typeof(ItemInfo))]
[XmlInclude(typeof(ItemInfoA))]
[XmlInclude(typeof(ItemInfoB))]
public class ItemInfoA : ItemInfo
{
...
}
[Serializable]
[XmlInclude(typeof(ItemInfo))]
[XmlInclude(typeof(ItemInfoA))]
[XmlInclude(typeof(ItemInfoB))]
public class ItemInfoB : ItemInfo
{
...
}
The class itemInfo describes a container which can hold other itemInfo objects in the array list, the parentItemInfo describes which is the parent container of the item info.
Since ItemInfoA and ItemInfoB derive from ItemInfo they can also be a member of the array list and the parentItemInfo, therefore when trying to serialize these objects (which can hold many objects in hierarchy) it fails with exception
IvvalidOperationException.`there was an error generating the xml file `
My question is:
What attributes do I need to add the ItemInfo class so it will be serializable?
Note: the exception is only when the ItemInfo[A]/[B] are initialized with parentItemInfo or the arrayList.
Help please!
Thanks!
With the edited question, it looks like you have a loop. Note that XmlSerializer is a tree serializer, not a graph serializer, so it will fail. The usual fix here is to disable upwards traversal:
[XmlIgnore]
public ItemInfo parentItemInfo;
Note you will have to manually fixup the parents after deserialization, of course.
Re the exception - you need to look at the InnerException - it probably tells you exactly this, for example in your (catch ex):
while(ex != null) {
Debug.WriteLine(ex.Message);
ex = ex.InnerException;
}
I'm guessing it is actually:
"A circular reference was detected while serializing an object of type ItemInfoA."
More generally on the design, honestly that (public fields, ArrayList, settable lists) is bad practice; here's a more typical re-write that behaves identically:
[DefaultPropertyAttribute("Name")]
[XmlInclude(typeof(ItemInfoA))]
[XmlInclude(typeof(ItemInfoB))]
public class ItemInfo
{
[XmlElement("name")]
public string Name { get; set; }
private readonly List<ItemInfo> items = new List<ItemInfo>();
public List<ItemInfo> Items { get { return items; } }
[XmlIgnore]
public ItemInfo ParentItemInfo { get; set; }
}
public class ItemInfoA : ItemInfo
{
}
public class ItemInfoB : ItemInfo
{
}
as requested, here's a general (not question-specific) illustration of recursively setting the parents in a hive (for kicks I'm using depth-first on the heap; for bredth-first just swap Stack<T> for Queue<T>; I try to avoid stack-based recursion in these scenarios):
public static void SetParentsRecursive(Item parent)
{
List<Item> done = new List<Item>();
Stack<Item> pending = new Stack<Item>();
pending.Push(parent);
while(pending.Count > 0)
{
parent = pending.Pop();
foreach(var child in parent.Items)
{
if(!done.Contains(child))
{
child.Parent = parent;
done.Add(child);
pending.Push(child);
}
}
}
}

How to code a truly generic tree using Generics

Lets say I have a Node class as follows:
class Node<T>
{
T data;
List<Node<T>> children;
internal Node(T data)
{
this.data = data;
}
List<Node<T>> Children
{
get
{
if (children == null)
children = new List<Node<T>>(1);
return children;
}
}
internal IEnumerable<Node<T>> GetChildren()
{
return children;
}
internal bool HasChildren
{
get
{
return children != null;
}
}
internal T Data
{
get
{
return data;
}
}
internal void AddChild(Node<T> child)
{
this.Children.Add(child);
}
internal void AddChild(T child)
{
this.Children.Add(new Node<T>(child));
}
}
The problem is that each and every node of the tree is confined to a single type. However, there are situations where the root node is of one type, which has children of another type which has children of a third type (example documents-->paragraphs-->lines-->words).
How do you define a generic tree for such cases?
If you want a strict hierarchy of types you could declare them like this:
class Node<T, TChild> {...}
Node<Document, Node<Paragraph, Node<Line, Word>>>
I did not claim it would be pretty. :)
How do you define a generic tree for such cases?
I wouldn't try to in the first place. If what I wanted to model was:
I have a list of documents
A document has a list of paragraphs
A paragraph has a list of words
then why do you need generic nodes at all? Make a class Paragraph that has a List<Word>, make a class Document that has a List<Paragraph>, and then make a List<Document> and you're done. Why do you need to artificially impose a generic tree structure? What benefit does that buy you?
Have all of your sub-objects implement a specific eg IDocumentPart then declare Node
I have been reluctant to offer the code example attached, feeling that I don't have a strong sense, yet, of the "norms" of StackOverFlow in terms of posting code that may be "speculative," and, feeling that this particular frolic is some form of "mutant species" escaped from the laboratory on "The Island of Dr. Moreau" :) And, I do think the answer by Eric Lippert above is right-on.
So please take what follows with "a grain of salt" as just an experiment in "probing" .NET inheritance (uses FrameWork 3.5 facilities). My goal in writing this (a few months ago) was to experiment with an Abstract Class foundation for Node structure that implemented an internal List<> of "itself," then implement strongly-typed classes that inherited from the Abstract class ... and, on that foundation, build a generalized Tree data structure.
In fact I was surprised when I tested this, that it worked ! :)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// experimental code : tested to a limited extent
// use only for educational purposes
namespace complexTree
{
// foundation abstract class template
public abstract class idioNode
{
// a collection of "itself" !
public List<idioNode> Nodes { private set; get; }
public idioNode Parent { get; set; }
public idioNode()
{
Nodes = new List<idioNode>();
}
public void Add(idioNode theNode)
{
Nodes.Add(theNode);
theNode.Parent = this;
}
}
// strongly typed Node of type String
public class idioString : idioNode
{
public string Value { get; set; }
public idioString(string strValue)
{
Value = strValue;
}
}
// strongly typed Node of type Int
public class idioInt : idioNode
{
public int Value { get; set; }
public idioInt(int intValue)
{
Value = intValue;
}
}
// strongly type Node of a complex type
// note : this is just a "made-up" test case
// designed to "stress" this experiment
// it certainly doesn't model any "real world"
// use case
public class idioComplex : idioNode
{
public Dictionary<idioString, idioInt> Value { get; set; }
public idioComplex(idioInt theInt, idioString theString)
{
Value = new Dictionary<idioString, idioInt>();
Value.Add(theString, theInt);
}
public void Add(idioInt theInt, idioString theString)
{
Value.Add(theString, theInt);
theInt.Parent = this;
theString.Parent = this;
}
}
// special case the Tree's root nodes
// no particular reason for doing this
public class idioTreeRootNodes : List<idioNode>
{
public new void Add(idioNode theNode)
{
base.Add(theNode);
theNode.Parent = null;
}
}
// the Tree object
public class idioTree
{
public idioTreeRootNodes Nodes { get; set; }
public idioTree()
{
Nodes = new idioTreeRootNodes();
}
}
}
So, to the test : (call this code from some EventHandler on a WinForm) :
// make a new idioTree
idioTree testIdioTree = new idioTree();
// make a new idioNode of type String
idioString testIdioString = new idioString("a string");
// add the Node to the Tree
testIdioTree.Nodes.Add(testIdioString);
// make a new idioNode of type Int
idioInt testIdioInt = new idioInt(99);
// add to Tree
testIdioTree.Nodes.Add(testIdioInt);
// make another idioNode of type String
idioString testIdioString2 = new idioString("another string");
// add the new Node to the child Node collection of the Int type Node
testIdioInt.Nodes.Add(testIdioString2);
// validate inheritance can be verified at run-time
if (testIdioInt.Nodes[0] is idioString) MessageBox.Show("it's a string, idiot");
if (!(testIdioInt.Nodes[0] is idioInt)) MessageBox.Show("it's not an int, idiot");
// make a new "complex" idioNode
// creating a Key<>Value pair of the required types of idioNodes
idioComplex complexIdio = new idioComplex(new idioInt(88), new idioString("weirder"));
// add a child Node to the complex idioNode
complexIdio.Add(new idioInt(77), new idioString("too weird"));
// make another idioNode of type Int
idioInt idioInt2 = new idioInt(33);
// add the complex idioNode to the child Node collection of the new Int type idioNode
idioInt2.Nodes.Add(complexIdio);
// add the new Int type Node to the Tree
testIdioTree.Nodes.Add(idioInt2);
// validate you can verify the type of idioComplex at run-time
MessageBox.Show(" tree/2/0 is complex = " + (testIdioTree.Nodes[2].Nodes[0] is idioComplex).ToString());
If the "smell" of this code is as bad as the fruit that here in Thailand we call the "durian" : well, so be it :) An obvious possible "weirdness" in this experiment is that you could have references to the same Node in more than one place in the tree at the same time.

Categories