Переопределить функцию ToString класса

У меня есть класс с именем 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();         
  }
}

Я хотел бы знать, есть ли лучший способ преодолеть все свойства Myclass с заявлением о порядке реализации ToString функция.

3 ответа

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)));

Это использует Linq Select и перегрузка GetValue из.NET 4.5 (Visual Studio 2012).

Нет, нет никакого способа сделать это по вашим требованиям, кроме как вручную написать каждую функцию. Было бы легко с отражением, но

Метод GetFields не возвращает поля в определенном порядке, например в алфавитном порядке или в порядке объявления. Ваш код не должен зависеть от порядка, в котором возвращаются поля, потому что этот порядок меняется.

Таким образом, нет способа фактически получить порядок декларации. Вы можете попробовать в алфавитном порядке, заказав их самостоятельно, хотя.

.Net предлагает объект XmlSerializer, предназначенный для предоставления вам представления вашего объекта. Вы можете воспользоваться этим, просто захватывая текстовые узлы и соединяя их:

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);
}

Вызов ToString для свойств, которые являются сложными классами, является более сложным... для этого используйте атрибут XmlElement для этих свойств, чтобы сериализатор знал, что вы хотите вывести эти свойства, а затем разрешите неявное преобразование в строку, чтобы сериализатор не ошибался. Как ни странно, вам также нужно реализовать неявное преобразование из строки (я думаю, потому что сериализатор также имеет возможность десериализации); но это не должно работать. Очень хакерский.

Альтернативный вариант - сделать ваш дочерний тип сериализуемым, используя [Serializable] атрибут, положить [XmlIgnore] в любых общедоступных свойствах затем создайте свойство с помощью метода get, вызывающего функцию ToString, и метода фиктивного набора (снова, чтобы обмануть сериализатор). Не отлично, но это работает.

Рабочий пример

using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Xml.XPath;
namespace Stackru
{
    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();
        }
    }

}

альтернатива

[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:

  • Если у вас есть дочерние элементы сложных типов (например, P5 в приведенном выше примере), как вы хотите, чтобы они обрабатывались? Должны ли их свойства также разделяться трубами (в каком случае, как вы узнаете, какие свойства относятся к каким объектам)?
  • Как следует обрабатывать нулевые значения - так же, как пробелы (то есть два канала, между которыми ничего нет) или вообще не выводить?
  • Было бы лучше возвращать имена свойств, а также значения - чтобы вы не зависели от порядка / вашего вывода, более устойчивых к будущим изменениям в определении класса?
  • Возможно, XMLSerializer сам по себе лучше соответствует вашим основным требованиям; предполагая, что вы хотите надежный способ представления данных вашего объекта?
Другие вопросы по тегам