I have a class named Myclass
with an override of ToString()
like this:
class Field
{
}
class MyClass
{
Field propretie1
Field propretie2
.
.
.
Field propretie15
public override string ToString()
{
StringBuilder temp = new StringBuilder();
temp.Append(propretie1.ToString())
temp.Append("|");
temp.Append(propretie2.ToString())
temp.Append("|");
.
.
temp.Append(propretie15.ToString())
return temp.ToString();
}
}
I'd like to know if there is a better way to get over all the properties of Myclass
with the declaration order to implement the ToString
function.
No, there is no way to do this by your requirements other than coding each function manually. It would be easy with reflection, but
The GetFields method does not return fields in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which fields are returned, because that order varies.
So there is no way to actually get the order of the declaration. You could try alphabetical order by ordering them yourself though.
var allProps = typeof(MyClass).GetProperties(); // or GetFields() if they are fields
Array.Sort(allProps, (x, y) => x.Name.CompareTo(y.Name));
return string.Join("|", allProps.Select(x => x.GetValue(this)));
This uses Linq Select
and an overload of GetValue
from .NET 4.5 (Visual Studio 2012).
.Net offers an XmlSerializer object aimed at giving you a representation of your object. You can take advantage of that, just grabbing text nodes and joining them:
public override string ToString()
{
return this.Stringify(); //calls reusable code to "stringify" any object
}
//converts an object's properties to a string of pipe delimited values
public static string Stringify<T>(this T obj)
{
var xs = new XmlSerializer(obj.GetType());
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
xs.Serialize(writer, obj);
}
var s = from text in doc.XPathSelectElements("//*[./text()]") select text.Value;
return string.Join("|", s);
}
Calling ToString on properties which are complex classes is more complex... to do that use the XmlElement attribute on those properties so the serializer knows you want to output these properties, then allow implicit conversion to string so the serializer doesn't error. Weirdly you also need to implement implicit conversion from string too (I guess because the serializer has the ability to deserialize too); but that doesn't have to work. Very hacky.
An alternate is to make your child type serializable using the [Serializable]
attribute, put [XmlIgnore]
on any public properties, then create a property with a get method calling to ToString function, and a dummy set method (again to trick the serializer). Not great, but it works.
Working Example
using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Xml.XPath;
namespace StackOverflow
{
public static class ObjectStringer
{
public static string Stringify<T>(this T obj)
{
var xs = new XmlSerializer(obj.GetType());
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
xs.Serialize(writer, obj);
}
var s = from text in doc.XPathSelectElements("//*[./text()]") select text.Value;
return string.Join("|", s);
}
}
public class Field
{
static int x = 0;
int y;
public Field()
{
y = ++x;
}
public override string ToString()
{
return y.ToString();
}
public static implicit operator String(Field f)
{
return f==null?null:f.ToString();
}
public static implicit operator Field(String s)
{
return s ?? "nasty hack to make serializer work";
}
}
public class Demo
{
public string P1 { get; set; }
public string X { get; set; } //something to show if we're pulling back results in order defined here, or if the system just goes alphabetically
public string P2 { get; set; }
public int P3 { get; set; }
public DateTime P4 { get; set; }
public Demo P5 { get; set; }
[XmlElement(typeof(String))]
public Field P6 { get; set; }
[XmlElement(typeof(String))]
public Field P7 { get; set; }
public override string ToString()
{
return this.Stringify();
}
}
public class Program
{
public static void Main(string[] args)
{
Demo d = new Demo() { P1 = "test1", X = "expert mode", P2 = "test2", P3 = 3, P4 = DateTime.UtcNow, P5 = new Demo() { P1 = "baby", P2 = "ooh" },P6=new Field(),P7=new Field() };
//d.P5 = d; //this solution's not perfect - e.g. attempt to serialize a circular loop in the object's graph
Console.WriteLine(d.ToString());
Console.WriteLine("done");
Console.ReadKey();
}
}
}
Alternative
[Serializable]
public class Field
{
static int x = 0;
int y;
public string DummyToString { get { return this.ToString(); } set { /*serializer hack*/ } }
[XmlIgnore]
public string DontShowMe { get; set; }
public Field()
{
y = ++x;
DontShowMe = "you shouldn't see this";
}
public override string ToString()
{
return string.Format("string me on #{0}", y);
}
}
//Demo's Field properties no longer require XmlElement attributes; i.e.:
public Field P6 { get; set; }
public Field P7 { get; set; }
NB:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.