简体   繁体   English

覆盖类的ToString函数

[英]Override ToString function of class

I have a class named Myclass with an override of ToString() like this: 我有一个名为Myclass的类,它具有ToString()的重写,如下所示:

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. 我想知道是否有一种更好的方法可以用声明顺序来遍历Myclass所有属性,以实现ToString函数。

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. GetFields方法不按特定顺序(例如字母顺序或声明顺序)返回字段。 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). 这将使用Linq Select和.NET 4.5(Visual Studio 2012)的GetValue的重载。

.Net offers an XmlSerializer object aimed at giving you a representation of your object. .Net提供了一个XmlSerializer对象,旨在为您提供对象的表示形式。 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. 在复杂类的属性上调用ToString更为复杂...在这些属性上使用XmlElement属性可以做到这一点,以便序列化程序知道您要输出这些属性,然后允许隐式转换为字符串,因此序列化程序不会出错。 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. 很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). 另一种方法是使用[Serializable]属性使您的子类型可序列[Serializable] ,将[XmlIgnore]放在任何公共属性上,然后使用调用ToString函数的get方法和伪set方法创建属性(同样可以欺骗序列化程序) 。 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: 注意:

  • If you have child items which are complex types (eg P5 in the above example), how do you want them handled? 如果您有复杂类型的子项(例如,上例中的P5),则如何处理它们? Should their properties also be pipe delimited (in which case how do you know which properties relate to which objects)? 它们的属性也应该用管道定界吗(在这种情况下,您如何知道哪些属性与哪些对象有关)?
  • How should null values be handled - same as blanks (ie two pipes with nothing between them), or not output at all? 空值应该如何处理-与空白相同(即两个管道之间没有任何东西),或者根本不输出?
  • Would it be better to return the property names as well as values - so you're not reliant on the order / your output's more robust to future changes in class definition? 返回属性名称和值会更好-这样您就不必依赖顺序了/输出对类定义的将来更改更可靠了吗?
  • Perhaps the XMLSerializer on its own better suits your underlying requirement; 也许XMLSerializer本身更适合您的基本需求; assuming you want a robust way of representing your object's data? 假设您想要一种强大的方式来表示对象的数据?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM