DumpObject с GetFields и GetProperties, например, с вложенным классом

Это мой первый вопрос в stackru, и я новичок в использовании отражения.

Я хотел бы сбросить все значения экземпляра объекта для справки (чтобы отслеживать используемые значения в тесте). Я использую Compact Framework 3.5 не полный каркас. Имейте это в виду для ваших предложений.

Представьте себе следующие классы:

public class Camera : IDisposable
{
    public Camera.FilenameProperties SnapshotFile;
    public double DigitalZoomFactor { get; set; }
    public bool DisplayHistogram { get; set; }
    public int ImageUpdateInterval { get; set; }
    public Camera.ImprintCaptionPosType ImprintCaptionPos { get; set; }
    public string ImprintCaptionString { get; set; }
}

где "специальные" типы:

    public class FilenameProperties
    {
        public string Directory { get; set; }
        public string Filename { get; set; }
        public Camera.FilenamePaddingType FilenamePadding { get; set; }
        public Camera.ImageType ImageFormatType { get; set; }
        public Camera.ImageResolutionType ImageResolution { get; set; }
        public int JPGQuality { get; set; }

        public void Restore();
        public void Save();

        public enum Fnametype
        {
            tSnapshot = 0,
            tCircularCapture = 1,
        }
    }
    public enum ImprintCaptionPosType
    {
        Disabled = 0,
        LowerRight = 1,
        LowerLeft = 2,
        LowerCenter = 3,
        UpperRight = 4,
        UpperLeft = 5,
        UpperCenter = 6,
        Center = 7,
    }

Теперь я могу получить "базовые" имена и свойства и имена полей экземпляра камеры:

Camera cam = new Camera();
dumpProperties(cam);
...
    void dumpProperties(object oClass)
    {
        System.Diagnostics.Debug.WriteLine(oClass.ToString());

        FieldInfo[] _Info = oClass.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
        for(int i = 0; i<_Info.Length; i++)
        {
            System.Diagnostics.Debug.WriteLine(_Info[i].Name + ":'" + _Info[i].GetValue(oClass).ToString()+"'");
        }

        foreach (PropertyInfo pi in oClass.GetType().GetProperties()) 
        {
            System.Diagnostics.Debug.WriteLine(pi.Name + ":'" + pi.GetValue(oClass, null) 
                + "' Type=" + pi.PropertyType.ToString());
        }
    }

а затем получить что-то вроде этого:

Intermec.Multimedia.Camera
SnapshotFile:'Intermec.Multimedia.Camera+FilenameProperties'
DigitalZoomFactor:'1' Type=System.Double
DisplayHistogram:'False' Type=System.Boolean
ImageUpdateInterval:'1' Type=System.Int32
ImprintCaptionPos:'Disabled' Type=Intermec.Multimedia.Camera+ImprintCaptionPosType
ImprintCaptionString:'' Type=System.String

Теперь для простых свойств, таких как DigitalZoomFactor и ImageUpdateInterval, я получаю то, что мне нужно, но для вложенного класса (правильная формулировка?) Я получаю только тип, как, например, в SnapshotFile. Для вложенного перечисления я получаю значение как с 'ImprintCaptionPos'.

Как я могу получить значения вложенных значений, таких как FilenameProperties.Filename поля / свойства SnapshotFile?

Если я использую dumpProperties(cam.SnapshotFile), я получаю вывод, который ищу:

Intermec.Multimedia.Camera+FilenameProperties
Directory:'\Program Files\FrmCamera' Type=System.String
Filename:'myphoto' Type=System.String
ImageFormatType:'JPG' Type=Intermec.Multimedia.Camera+ImageType
FilenamePadding:'None' Type=Intermec.Multimedia.Camera+FilenamePaddingType
ImageResolution:'Medium' Type=Intermec.Multimedia.Camera+ImageResolutionType
JPGQuality:'100' Type=System.Int32

Но как я могу автоматизировать это?

Я много занимался поиском и тестированием, но не смог найти решение. Кажется, проблема заключается в том, что экземпляр поля может проходить через него.

У меня нет исходного кода класса Camera, поэтому я не могу добавлять или удалять код там.

Кто-нибудь может помочь?

Мне нужно получить что-то вроде шоу отладчика:введите описание изображения здесь

3 ответа

Решение

Хорошо, я нашел способ (обходной путь) получить все свойства (в XML, но кого это волнует), используя код здесь:

Вывод похож на xml, но приемлем для меня. Вот выдержка:

<xml version="1.0" encoding="utf-8">
<Camera xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
...
    <ImprintCaptionPos>Disabled</ImprintCaptionPos>
        <SnapshotFile>
        <Directory>\\Program Files\\FrmCamera</Directory>
        <Filename>myphoto</Filename>
        <ImageFormatType>JPG</ImageFormatType>
        <FilenamePadding>None</FilenamePadding>
        <ImageResolution>Medium</ImageResolution>
        <JPGQuality>100</JPGQuality>
    </SnapshotFile>
...

В моем коде мне просто нужно позвонить

        string s = serialization.mySerialize.SerializeObject<Intermec.Multimedia.Camera>(cam);

Чтобы получить "дамп" всех текущих свойств экземпляра.

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

Спасибо

Josef

Вам просто нужно использовать рекурсию и вернуться к методу, если ваше свойство является классом. Вот пример используемой нами процедуры сериализации XML, которая эффективно просматривает свойства цели с помощью отражения и генерирует XElement от него. Ваша логика будет несколько иной, поскольку вы не собираетесь создавать XML, но структура того, что вы собираетесь делать, будет довольно схожей.

public XElement Serialize(object source, 
                          string objectName, 
                          bool includeNonPublicProperties)
{
    XElement element;
    var flags = BindingFlags.Instance | BindingFlags.Public;
    if(includeNonPublicProperties) 
    {
        flags |= BindingFlags.NonPublic;
    }

    var props = source.GetType().GetProperties(flags);

    var type = source.GetType();

    string nodeName;
    if(objectName == null)
    {
        if (type.IsGenericType)
        {
            nodeName = type.Name.CropAtLast('`');
        }
        else
        {
            nodeName = type.Name;
        }            
    }
    else
    {
        nodeName = objectName;
    }

    element = new XElement(nodeName);

    foreach (var prop in props)
    {
        string name = prop.Name;
        string value = null;
        bool valIsElement = false;

        if (!prop.CanRead) continue;

        if(prop.PropertyType.IsEnum)
        {
            value = prop.GetValue(source, null).ToString();
        }
        else 
        {
            string typeName;

            if (prop.PropertyType.IsNullable())
            {
                typeName = prop.PropertyType.GetGenericArguments()[0].Name;
            }
            else
            {
                typeName = prop.PropertyType.Name;
            }

            switch (typeName)
            {
                case "String":
                case "Boolean":
                case "Byte":
                case "TimeSpan":
                case "Single":
                case "Double":
                case "Int16":
                case "UInt16":
                case "Int32":
                case "UInt32":
                case "Int64":
                case "UInt64":
                    value = (prop.GetValue(source, null) ?? string.Empty).ToString();
                    break;
                case "DateTime":
                    try
                    {
                        var tempDT = Convert.ToDateTime(prop.GetValue(source, null));
                        if (tempDT == DateTime.MinValue) continue;
                        value = tempDT.ToString("MM/dd/yyyy HH:mm:ss.fffffff");
                    }
                    catch(Exception ex)
                    {
                        continue;
                    }
                    break;
                default:
                    var o = prop.GetValue(source, null);
                    XElement child;
                    if (o == null)
                    {
                        child = new XElement(prop.Name);
                    }
                    else
                    {
                        child = Serialize(o, prop.Name, includeNonPublicProperties);
                    }

                    element.Add(child);
                    valIsElement = true;
                    break;
            }
        }

        if (!valIsElement)
        {
            element.AddAttribute(name, value);
        }
    }

    return element;
}

Вы должны выполнить итерацию для всех внутренних членов объекта, как вы делали для внешнего класса. Это будет упражнение для вас, чтобы реализовать его для полного набора типов.NET. Ниже приведен псевдокод простой реализации

void dumpProperties(object target)
{
    if (target.GetType().IsSimple() || target.GetType().IsMethodImplemented("ToString"))
        System.Diagnostics.Debug.WriteLine(target.ToString());
    else if (target is IEnumerable)
    {
        foreach (object item in (IEnumerable)target)
        {
            System.Diagnostics.Debug.WriteLine(dumpProperties(target));
        }
    }
    else
    {
        foreach (FieldInfo fieldInfo in target.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance))
        {
             System.Diagnostics.Debug.WriteLine(dumpProperties(fieldInfo.FieldHandle.Value));
        }
        foreach (PropertyInfo propertyInfo in oClass.GetType().GetProperties())
        {
                  System.Diagnostics.Debug.WriteLine(dumpProperties(propertyInfo.GetGetMethod().MethodHandle.Value));
        }
    }
}

Или вы можете использовать Cinchoo Framework, который имеет встроенную функцию для выгрузки любого объекта.

Console.WriteLine(ChoObject.ToString(camera));
Другие вопросы по тегам