Large knowledge gap wrt to handling of types

H

Helmut Giese

Hello out there,
I don't know how to pass a 'type' as a parameter and therefore
currently have this code to serialize components to XML:

TypeConverter cvt;
string str;
foreach (PropertyItem pItem in props) {
switch (pItem.DisplayName) {
case "Anchor":
cvt = TypeDescriptor.GetConverter(typeof(AnchorStyles));
str = cvt.ConvertToInvariantString(pItem.Value);
break;
case "Cursor":
cvt = TypeDescriptor.GetConverter(typeof(Cursor));
str = cvt.ConvertToInvariantString(pItem.Value);
break;
case "Font":
cvt = TypeDescriptor.GetConverter(typeof(Font));
str = cvt.ConvertToInvariantString(pItem.Value);
break;
<etc. etc. etc.>

Reading it back is done like this
// reader: XMLReader
// self: the control created via its type read from XML
propName = reader.Name;
switch (propName) {
case "Anchor":
cvt = TypeDescriptor.GetConverter(typeof(AnchorStyles));
self.Anchor =
(AnchorStyles)cvt.ConvertFromInvariantString(str);
break;
case "Cursor":
cvt = TypeDescriptor.GetConverter(typeof(Cursor));
self.Cursor =
(Cursor)cvt.ConvertFromInvariantString(str);
break;
case "Font":
cvt = TypeDescriptor.GetConverter(typeof(Font));
self.Font = (Font)cvt.ConvertFromInvariantString(str);
break;
<etc. etc. etc.>

I hate code like this - endlessly repeating the same thing over and
over. However, I don't know how to do it otherwise, so I just lived
with it.

Now, however, I am integrating components from National Instrument's
Measurement Studio and they come with loads of properties of their
own. I could of course repeat the same code pattern another 50 times,
but I'd be much happier if someone could tell me how to put this code
into a function passing the type as parameter (and "somehow" handling
the type cast when reading).

Any insight into this magic will be greatly appreciated.
Best regards
Helmut Giese
 
M

Mark Rogers

Helmut said:
Hello out there,
I don't know how to pass a 'type' as a parameter and therefore
currently have this code to serialize components to XML:

TypeConverter cvt;
string str;
foreach (PropertyItem pItem in props) {
switch (pItem.DisplayName) {
case "Anchor":
cvt = TypeDescriptor.GetConverter(typeof(AnchorStyles));
str = cvt.ConvertToInvariantString(pItem.Value);
break;
case "Cursor":
cvt = TypeDescriptor.GetConverter(typeof(Cursor));
str = cvt.ConvertToInvariantString(pItem.Value);
break;
case "Font":
cvt = TypeDescriptor.GetConverter(typeof(Font));
str = cvt.ConvertToInvariantString(pItem.Value);
break;
<etc. etc. etc.>

Reading it back is done like this
// reader: XMLReader
// self: the control created via its type read from XML
propName = reader.Name;
switch (propName) {
case "Anchor":
cvt = TypeDescriptor.GetConverter(typeof(AnchorStyles));
self.Anchor =
(AnchorStyles)cvt.ConvertFromInvariantString(str);
break;
case "Cursor":
cvt = TypeDescriptor.GetConverter(typeof(Cursor));
self.Cursor =
(Cursor)cvt.ConvertFromInvariantString(str);
break;
case "Font":
cvt = TypeDescriptor.GetConverter(typeof(Font));
self.Font = (Font)cvt.ConvertFromInvariantString(str);
break;
<etc. etc. etc.>

I hate code like this - endlessly repeating the same thing over and
over. However, I don't know how to do it otherwise, so I just lived
with it.

Now, however, I am integrating components from National Instrument's
Measurement Studio and they come with loads of properties of their
own. I could of course repeat the same code pattern another 50 times,
but I'd be much happier if someone could tell me how to put this code
into a function passing the type as parameter (and "somehow" handling
the type cast when reading).

Any insight into this magic will be greatly appreciated.
Best regards
Helmut Giese

Sounds like a good case for Generics. I had the same problem a few
months ago. See these:

http://shiman.wordpress.com/2008/07/24/serialization-for-generic-type-in-c-net/
http://msdn.microsoft.com/en-us/library/512aeb7t.aspx

There's numerous other examples when you search "C# Generic Serialization"
 
P

Peter Duniho

Helmut said:
[...] I'd be much happier if someone could tell me how to put this code
into a function passing the type as parameter (and "somehow" handling
the type cast when reading).

Any insight into this magic will be greatly appreciated.

See the System.Type class. The "typeof" operator returns an instance of
that class, on any object you can call the GetType() method to return
the instance of the Type corresponding to that object, and of course the
PropertyItem class itself has the PropertyType property, returning the
type of the property itself.

For the deserialization, you can either use a generic method to handle
the casting, or you can just use reflection and call
PropertyInfo.SetValue(), basically the inverse of your serialization
code (currently, the two implementations are not quite symmetric).

Since you have to use reflection just to get the type of the property
during deserialization (well, technically you don't, but based on the
way the code is now, you probably will), I think you might as well
continue to use reflection to actually set the property.

Of course, all this assumes that you have a good reason for writing all
this custom serialization. The built-in .NET XML serialization will
handle all of this for public properties, without any additional effort
on your part.

Pete
 
F

Family Tree Mike

Hello out there,
I don't know how to pass a 'type' as a parameter and therefore
currently have this code to serialize components to XML:

TypeConverter cvt;
string str;
foreach (PropertyItem pItem in props) {
switch (pItem.DisplayName) {
case "Anchor":
cvt = TypeDescriptor.GetConverter(typeof(AnchorStyles));
str = cvt.ConvertToInvariantString(pItem.Value);
break;
case "Cursor":
cvt = TypeDescriptor.GetConverter(typeof(Cursor));
str = cvt.ConvertToInvariantString(pItem.Value);
break;
case "Font":
cvt = TypeDescriptor.GetConverter(typeof(Font));
str = cvt.ConvertToInvariantString(pItem.Value);
break;
<etc. etc. etc.>

Reading it back is done like this
// reader: XMLReader
// self: the control created via its type read from XML
propName = reader.Name;
switch (propName) {
case "Anchor":
cvt = TypeDescriptor.GetConverter(typeof(AnchorStyles));
self.Anchor =
(AnchorStyles)cvt.ConvertFromInvariantString(str);
break;
case "Cursor":
cvt = TypeDescriptor.GetConverter(typeof(Cursor));
self.Cursor =
(Cursor)cvt.ConvertFromInvariantString(str);
break;
case "Font":
cvt = TypeDescriptor.GetConverter(typeof(Font));
self.Font = (Font)cvt.ConvertFromInvariantString(str);
break;
<etc. etc. etc.>

I hate code like this - endlessly repeating the same thing over and
over. However, I don't know how to do it otherwise, so I just lived
with it.

Now, however, I am integrating components from National Instrument's
Measurement Studio and they come with loads of properties of their
own. I could of course repeat the same code pattern another 50 times,
but I'd be much happier if someone could tell me how to put this code
into a function passing the type as parameter (and "somehow" handling
the type cast when reading).

Any insight into this magic will be greatly appreciated.
Best regards
Helmut Giese

I would do something like this (typed freehand):

foreach (PropertyItem pItem in props)
{
XmlSerializer s = new XmlSerializer(pItem.GetType());
s.Serialize(stream, pItem);
}
 
H

Helmut Giese

On Fri, 11 Dec 2009 14:05:33 -0800, Mark Rogers

Hi Mark,
Sounds like a good case for Generics. I had the same problem a few
months ago. See these:

http://shiman.wordpress.com/2008/07/24/serialization-for-generic-type-in-c-net/
http://msdn.microsoft.com/en-us/library/512aeb7t.aspx

There's numerous other examples when you search "C# Generic Serialization"
I am a little bit pressed wrt time and would rather at the moment
avoid entering into something new. However, I'll tuck it away for
further reference.
Thanks and best regards
Helmut Giese
 
H

Helmut Giese

On Fri, 11 Dec 2009 14:07:26 -0800, Peter Duniho

Hi Peter,
See the System.Type class. The "typeof" operator returns an instance of
that class, on any object you can call the GetType() method to return
the instance of the Type corresponding to that object, and of course the
PropertyItem class itself has the PropertyType property, returning the
type of the property itself.
I can write out the properties to XML like this:

PropertyInfo[] props = ctrl.GetType().GetProperties();
foreach (PropertyInfo p in props)
writer.WriteElementString(p.Name, p.GetValue(ctrl,
null).ToString());

but this is probably not the way to do it: I am unable to read it
back, if the property is 'complex' or 'nested'. E.g. 'Margin' results
in
For the deserialization, you can either use a generic method to handle
the casting, or you can just use reflection and call
PropertyInfo.SetValue(), basically the inverse of your serialization
code (currently, the two implementations are not quite symmetric).
For reading I tried this

name = reader.Name;
str = reader.ReadElementContentAsString();
currItem = null;
foreach (PropertyInfo pItem in realProps) {
if (name == pItem.Name) {
currItem = pItem;
break;
}
}
if (currItem != null) {
if (pType.Equals(typeof(String)))
currItem.SetValue(self, str, null);
else if (pType.IsEnum)
currItem.SetValue(self, Enum.Parse(pType, str), null);
else
currItem.SetValue(self, Convert.ChangeType(str, pType), null);
}

and get an exception with properties like 'Margin' mentioned above.
So evidently I'm doing something wrong.
You don't happen to have a link to an example doing writing as well as
reading?
Of course, all this assumes that you have a good reason for writing all
this custom serialization. The built-in .NET XML serialization will
handle all of this for public properties, without any additional effort
on your part.
I am all for avoiding 'additional effort on my part' - however I am
serializing GUI objects. My 'good reason' is that my first attempts
failed because controls are not serializable.
What did I overlook?

Thanks for any further advice and best regards
Helmut Giese
 
H

Helmut Giese

Hi,
I would do something like this (typed freehand):

foreach (PropertyItem pItem in props)
{
XmlSerializer s = new XmlSerializer(pItem.GetType());
s.Serialize(stream, pItem);
}
looks cool - couldn't get any easier.
Creating an XmlSerializer for every property - ok, why not?
But how would you read it back?

Thanks for any further advice and best regards
Helmut Giese
 
F

Family Tree Mike

Hi,
looks cool - couldn't get any easier.
Creating an XmlSerializer for every property - ok, why not?
But how would you read it back?

Thanks for any further advice and best regards
Helmut Giese

Something like the following:

Inside a loop until you reach an end element:

reader.Read(); // moves to next element
string typename = reader.Name; // gets the next object type
// create the full name
Type t = Type.GetType(string.Format("SomeNamespace.{0}", typename));
XmlSerializer s = new XmlSerializer(t); // Serializer for that type
props.Add((PropertyItem) s.Deserialize(reader));
 
P

Peter Duniho

Helmut said:
On Fri, 11 Dec 2009 14:07:26 -0800, Peter Duniho

Hi Peter,
See the System.Type class. The "typeof" operator returns an instance of
that class, on any object you can call the GetType() method to return
the instance of the Type corresponding to that object, and of course the
PropertyItem class itself has the PropertyType property, returning the
type of the property itself.
I can write out the properties to XML like this:

PropertyInfo[] props = ctrl.GetType().GetProperties();
foreach (PropertyInfo p in props)
writer.WriteElementString(p.Name, p.GetValue(ctrl,
null).ToString());

My reply was specifically in regards to the code you posted. The above
is quite different. If you'd like to pursue another line of
questioning, it would be better if you could be more explicit about the
change in direction.
but this is probably not the way to do it: I am unable to read it
back, if the property is 'complex' or 'nested'. E.g. 'Margin' results
in
<Margin>{Left=3,Top=3,Right=3,Bottom=3}</Margin>
and I have no clue how to read it back.

Serializing complex types will require either than they support
serialization implicitly, or that you write reflection-based code to do
it manually.

This seems completely independent of your original question.
For reading I tried this

name = reader.Name;
str = reader.ReadElementContentAsString();
currItem = null;
foreach (PropertyInfo pItem in realProps) {
if (name == pItem.Name) {
currItem = pItem;
break;
}
}

Why not just retrieve the specific PropertyInfo object by name, using
the GetProperty() method?
if (currItem != null) {
if (pType.Equals(typeof(String)))
currItem.SetValue(self, str, null);
else if (pType.IsEnum)
currItem.SetValue(self, Enum.Parse(pType, str), null);
else
currItem.SetValue(self, Convert.ChangeType(str, pType), null);
}

and get an exception with properties like 'Margin' mentioned above.
So evidently I'm doing something wrong.
You don't happen to have a link to an example doing writing as well as
reading?

Sorry...I don't have a specific link to any of this, other than what
you'd find on MSDN anyway.
I am all for avoiding 'additional effort on my part' - however I am
serializing GUI objects. My 'good reason' is that my first attempts
failed because controls are not serializable.
What did I overlook?

Nothing that I know of. Your original post mentioned nothing at all
about what types you're serializing. Obviously, if you can't depend on
the built-in serialization to work, you'll have to do something else.

It's not clear from your original post what the type converter is doing
or where it came from. But, assuming you have a working mechanism to
convert things like a Windows Forms Cursor object or a Font object to
and from a string, the only remaining thing to do to modify the original
code you posted is to stop using the case statement and use reflection
to automate the mappings from properties to their names and back again.

For example, based on your original code, writing:

foreach (PropertyItem pItem in props)
{
string str = TypeDescriptor
.GetConverter(pItem.PropertyType)
.ConvertToInvariantString(pItem.GetValue(self, null));

// write "str" to your XML, using pItem.Name to
// tag the XML somehow (e.g. as an element name)
}

And reading:

PropertyInfo pItem = self.GetType().GetProperty(reader.Name);

// advance XmlReader to text node

pItem.SetValue(self, TypeDescriptor
.GetConverter(pItem.PropertyType)
.ConvertFromInvariantString(reader.Value), null);

I've left out the specifics of the XML read/write stuff, because a) it's
not clear how exactly you're handling that, and b) it doesn't seem
related to your question (I assume you are successfully writing and
reading XML elements and their inner text).

Note also that the heavy lifting is really done in the type converter,
but then that was true in your original code. You didn't seem to be
asking about that originally, so I'm not addressing it here.

Pete
 
Top