Question

this is my first question in stackoverflow and I am a beginner in using reflection.

I would like to dump all values of an object instance for reference (to keep track about used values on a test). I am using Compact Framework 3.5 not the full framework. Keep that in mind for your suggestions.

Imagine following classes:

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

where the 'special' types are:

    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,
    }

Now, I can get the 'base' names and properties and the field names of an instance of camera:

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

and then get soemthing like this:

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

Now, for simple properties like DigitalZoomFactor and ImageUpdateInterval I get what I need, but for the nested class (correct wording?) I only get the type as for example with SnapshotFile. For the nested enum I get the value as with 'ImprintCaptionPos'.

How can I get the values of the nested values like FilenameProperties.Filename of the SnapshotFile field/property?

If I use dumpProperties(cam.SnapshotFile), I get the output I am looking for:

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

But how can I automate that?

I did a lot of search and test coding but was unable to find a solution. The problem seems to be getting the instance of the field to be able to iterate thru it.

I do not have the source code of the Camera class, so I cannot add or remove code in there.

Can anyone help?

I need to get something like the debugger shows: enter image description here

Was it helpful?

Solution 2

OK, I find a way (a workaround) to get all properties (in an XML but who cares) using the code from here:

The output is xml like but acceptable for me. Here an excerpt:

<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>
...

In my code I just have to call

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

To get a 'dump' of all current properties of the instance.

Thanks to all for your help. Possibly I was misunderstood with my question and reflection is unable to give what I want.

Thanks

Josef

OTHER TIPS

You simply need to use recursion, and loop back into the method if your property is a class. Here's an example of an XML Serialization routine we use, that effectively walks the properties of a target using reflection, and generates an XElement from it. Your logic would be somewhat different as you're not going to build up XML, but the structure of what you're going to do will be pretty similar.

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

You have to do the iteration to all inner object members like you did for outer class. Will be an exercise for you to implement it for complete set of .NET types. Below is the pseudo code of simple implementation

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

Or you can use Cinchoo framework, which has built in function to dump any object.

Console.WriteLine(ChoObject.ToString(camera));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top