Click to See Complete Forum and Search --> : Serialize LinkedList to XML - default accessor


T-Lo
June 13th, 2008, 10:38 AM
Hi everybody!

I am having the same problem as "jamessadlier" had in the following thread:
thtp://www.codeguru.com/forum/showthread.php?t=348850

The error i get while trying to serialize is the same expect with another type of collection (LinkedList).
{"You must implement a default accessor on System.Collections.Generic.LinkedList because it inherits from ICollection."}

"jamessadlier" solved his own problem and said: Fixed it. The Keyword "Default" has to be on the declaration of the accessor.

That is VB .net, but what would the C# equivalent?

I suspect that it is the c# keyword default? http://msdn.microsoft.com/en-us/library/xwth0h0d(VS.80).aspx


i tried to serialize MyLinkedList (code, see below), which i created by extending LinkedList. The problem is the same no matter wheter i use MyLinkedList or LinkedList.

now some code:

Class MyLinkedList.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace Example
{
public class MyLinkedList<T> : LinkedList<T>
{


new public void AddLast(T value)
{
base.AddLast(value);
SetFlags();
}
new public void AddFirst(T value)
{
base.AddFirst(value);
SetFlags();
}
new public void AddLast(LinkedListNode<T> node)
{
base.AddLast(node);
SetFlags();
}
new public void AddFirst(LinkedListNode<T> node)
{
base.AddFirst(node);
SetFlags();
}
new public void AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode)
{
base.AddAfter(node, newNode);
SetFlags();
}
new public void AddAfter(LinkedListNode<T> node, T value)
{
base.AddAfter(node, value);
SetFlags();
}
new public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
{
base.AddBefore(node, newNode);
SetFlags();
}
new public void AddBefore(LinkedListNode<T> node, T value)
{
base.AddBefore(node, value);
SetFlags();
}
new public void Clear()
{
base.Clear();
SetFlags();
}
new public void Remove(T value)
{
base.Remove(value);
SetFlags();
}
new public void Remove(LinkedListNode<T> node)
{
base.Remove(node);
SetFlags();
}
new public void RemoveFirst()
{
base.RemoveFirst();
SetFlags();
}
new public void RemoveLast()
{
base.RemoveLast();
SetFlags();
}

private void SetFlags()
{
//some stuff here
}
}
}


and the class containing MyLinkedList or LinkedList ( the one i want to serialize, its just the relevant part):

Settings.cs

public class UserSettings : XMLPersistence<UserSettings>
{

public UserSettings()
{
//some stuff here
}


//some properties (primitive types here)
private MyLinkedList<DayOfWeek> _workdays;

public MyLinkedList<DayOfWeek> Workdays
{
get { return _workdays; }
set { _workdays = value; }
}

//some properties (primitive types here)
}


and XMLPersistence.cs


public class XMLPersistence<T>
{

public static T Deserialize(string sPath)
{
XmlSerializer ser = new XmlSerializer(typeof(T));
StreamReader str = new StreamReader(sPath);
T to_read = (T)ser.Deserialize(str);
str.Close();
return to_read;
}

public void Serialize(string sPath)
{
XmlSerializer ser = new XmlSerializer(typeof(T));
FileStream str = new FileStream(sPath, FileMode.Create);
ser.Serialize(str, this);
str.Close();
}
}


so a short "test"-program could look like:

UserSettings cl1 = new UserSettings();
//fill cl1 with values
cl1.Serialize("C:\\test.xml");


So what do i need to change t omake that example fly?

it would be grat if someone would point me into the right direction. i hope this question is not too foolish. Thank in advance for your help!! :)

Cheers!

TheCPUWizard
June 13th, 2008, 11:03 AM
Aside from your question, do you realize the problems you are going to encounter due to your use of the "new" qualifier on your methods.

"new" should be used VERY rarely, as the associated behaviour is never what 90% of the people who use the class will expect...

MadHatter
June 13th, 2008, 12:02 PM
also that "new" methods hide base class methods, and are not polymorphic (if your class is casted to its base class, or referred to by its base class, it will not call the "new" method, but the "base" method).

the accessor referred to there is called an indexer which looks something like this:


public class MyList<T> : List<T>{

public T this[int index] {
get { return base[index]; }
set { base[index] = value; }
}

}

T-Lo
June 13th, 2008, 12:25 PM
TheCPUWizard:

as the associated behaviour is never what 90% of the people who use the class will expect...

@TheCPUWizard: so your point is that using the new modifier to hide an inherited member is not good because the changed behaviour will confuse other people reading/using my code? well, i am just setting some flags and pass the paramters (if applicable) to the according member of the base class. => the expectations are satisfied. the fact that the overwritten methods set some flags will be well documented.

anyway, i'll take some time and think about your point, because an unexpected side-effect (unexpected for those who do not read the documentation) might not be the best thing, i agree...

i'll take take that into consideration when revising my design :)

so thanks for your input. but being able to persist a LinkedList would be what i need anyway. so any other input here? thanks a lot!

EDIT:
obviously it took me too long to respond :D

@MadHatter: i have to admit that i didn't know and are not polymorphic (if your class is casted to its base class, or referred to by its base class, it will not call the "new" method, but the "base" method). that is an point you better know. it probably would have confused me at some point in the future. thanks a lot!

and thanks for the indexer. i already figured out that i have to use sth. called "indexer", but didn't know how to do that. i'll try that on monday. thanks again! *THUMBSUP*

TheCPUWizard
June 13th, 2008, 01:11 PM
... the expectations are satisfied....
i have to admit that i didn't know that is an point you better know. it probably would have confused me at some point in the future.

Do you see a conflict in these two parts of the post???

If you were not 100% aware and intending be design, that

LinkedList<T> myVar = new MyLinkedList<T> ();
myVar.AddLast(T); // Will NOT call SetFlags!!!

Then you do NOT understand the usage of "new".

T-Lo
June 13th, 2008, 03:15 PM
maybe it is because english is not my mothertongue, but all i wanted to say with my previous posts is the following:

i wasn't aware that:

LinkedList<T> myVar = new MyLinkedList<T> ();
myVar.AddLast(T); // Will NOT call SetFlags!!!

but it is good that i know it now. thanks for making me aware of the fact.

but:

MyLinkedList<T> myVar = new MyLinkedList<T> ();
myVar.AddLast(T); // WILL call SetFlags!!!

and that is what i want to achieve. an now i know that i have to avoid that MyLinkedList is cast to LinkedList.


Now to my problem with persisting a (My)LinkedList collection:


i tried to add

public T this[int index]
{
get { return base[index]; }
set { base[index] = value; }
}

to MyLinkedList.cs now i get the following error message:

"Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.LinkedList<T>'"

any ideas?

MadHatter
June 13th, 2008, 04:49 PM
doh, sorry, yea linked lists shouldn't have an indexer, only a head / tail (or first / last).

I honestly have no idea what that message is about, sorry I useless :(

TheCPUWizard
June 13th, 2008, 06:45 PM
i know that i have to avoid that MyLinkedList is cast to LinkedList.
And how are you going to ENFORCE that.

The point is that these methods are not meant to be overridden. If they were, they would have been declared virtual.

The correct way, is to create our class NOT derived from LinkedList<T>, but instead from the appropriate interfaces and AGGREGATE the imlementation of the LinkedList<T>.

This will be safe. And as a side effect will solve all of your serialization issues, since the original class was NOT designed to be used for that EITHER.

90%+ of all problems like this are caused simply by programmers trying to use ahammer to insert a screw....

T-Lo
June 14th, 2008, 05:44 AM
@MadHatter: thanks for your help anyway! :)

so is that class the screwdriver i need?

Class2.cs:


namespace ConsoleApplication1
{
using System;
using System.Collections;
using System.Runtime.Serialization;
[Serializable]
public class MyLinkedList<T> : ICollection, ICloneable
{
private Node head = new Node();
[NonSerialized]
private bool isEnumerating = false;
private int count = 0;
public MyLinkedList()
{
}
public MyLinkedList(ICollection collection)
{
foreach (object o in collection)
{
this.AddLast(o);
}
}

public void AddFirst(object item)
{
isEnumerating = false;
head.Next = new Node(head.Next);
head.Next.Value = item;
++count;
}
public object RemoveFirst()
{
isEnumerating = false;
object o = head.Next.Value;
head.Next = head.Next.Next;
--count;
return o;
}
public void AddLast(object item)
{
isEnumerating = false;
head.Next = new Node(head.Next);
head.Value = item;
head = head.Next;
head.Value = null;
++count;
}
public object RemoveLast()
{
isEnumerating = false;
Node removeNode = head;
while (head.Next != removeNode)
{
head = head.Next;
}
return this.RemoveFront();
}
public static MyLinkedList<T> operator +(
MyLinkedList<T> lhs,
object rhs
)
{
lhs.AddLast(rhs);
return lhs;
}
public static bool operator ==(
MyLinkedList<T> lhs,
MyLinkedList<T> rhs
)
{
return lhs.Equals(rhs);
}
public static bool operator !=(
MyLinkedList<T> lhs,
MyLinkedList<T> rhs
)
{
return !lhs.Equals(rhs);
}
public virtual void CopyTo(Array array, int index)
{
if (array == null)
{
throw new ArgumentNullException(
"Null array reference",
"array"
);
}

if (index < 0)
{
throw new ArgumentOutOfRangeException(
"Index is out of range",
"index"
);
}

if (array.Rank > 1)
{
throw new ArgumentException(
"Array is multi-dimensional",
"array"
);
}

foreach (object o in this)
{
array.SetValue(o, index);
index++;
}
}
public virtual int Count
{
get
{
return this.count;
}
}

public virtual bool IsSynchronized
{
get { return false; }
}
public virtual object SyncRoot
{
get { return this; }
}
public virtual IEnumerator GetEnumerator()
{
isEnumerating = true;
return new Enumerator(this);
}
public virtual object Clone()
{
return new MyLinkedList<T>(this);
}
public override bool Equals(object compare)
{

if (base.Equals(compare))
{
return true;
}
else
{
MyLinkedList<T> compareList = compare as MyLinkedList<T>;
if (compareList != null
&& this.Count == compareList.Count)
{
IEnumerator thisEnum = this.GetEnumerator();
IEnumerator compareEnum = compareList.GetEnumerator();
while (thisEnum.MoveNext() && compareEnum.MoveNext())
{
if (!thisEnum.Current.Equals(compareEnum.Current))
{
return false;
}
}
}
}
return false;
}
public override int GetHashCode()
{
int hashCode = 0;

foreach (object o in this)
{
hashCode ^= o.GetHashCode();
}

return hashCode;
}
[Serializable]
internal class Node
{
private object nodeValue;
private Node next;
public object Value
{
get { return this.nodeValue; }
set { this.nodeValue = value; }
}
public Node Next
{
get { return this.next; }
set { this.next = value; }
}
public Node()
{
this.next = this;
}
public Node(Node next)
{
this.next = next;
}
}
internal class Enumerator : IEnumerator
{
private MyLinkedList<T> list;
private Node current;
private bool isValid;
public Enumerator(MyLinkedList<T> list)
{
this.list = list;
this.current = list.head;
this.isValid = true;
}
private void CheckValid()
{
if (!isValid || !list.isEnumerating)
{
throw new InvalidOperationException();
}
}
public virtual bool MoveNext()
{
CheckValid();
current = current.Next;
if (current == list.head)
{
isValid = false;
}
return isValid;
}
public virtual void Reset()
{
if (!list.isEnumerating)
{
throw new InvalidOperationException();
}
current = list.head;
isValid = true;
}

public object Current
{
get
{
CheckValid();
return current.Value;
}
}
}
}
}


while serializing it complains about "no default accessor available", too. so how to serialize that class?

TheCPUWizard
June 14th, 2008, 07:21 AM
[QUOTE=T-Lo
while serializing it complains about "no default accessor available", too. so how to serialize that class?[/QUOTE]

"Looks" very good, (of course I did not fully verify the functionallity of every method. Now you are safe, and free to extend...

You have to provide a direct way of reaching individual elements for serialization in otder for the DEFAULT serialization process to work.

So (now that you have 100% control and it can not be subverted, it is fairly easy to write:

public T this[int]
{
get {...}
set {...}
}


Now as previously pointed you this really does not apply for general linked lists because of the efficiency (each call has to wal from the beginning).

But for serialization, you can count on the fact that these calls will (probably) be ordered. So simply by traqcking the last index and the last value, you can optimize).

An alternative would be to implement CUSTOM serialization. This allows you FULL control over the process. Simply add ISerializable to your list of interfaces to indicate that you (not the system) will control the process and implement the interfaces methods.

T-Lo
June 25th, 2008, 03:25 AM
Ok. Thanks a lot to everybody who helped me. Great forum!