2

I'm trying to write a reflection class (for custom serialization). I'm stumped on the syntax for iterating through an array. The following works for non-arrays, but I'm missing the array part, and couldn't puzzle it out from other answers here (some of which do not compile)...

To implement deserialization, write-able references to each object in the target class must be obtained in order to set their values from the previously-serialized data.

Thanks in advance for any tips !

    private static void diagPrint(Object val)
    {
        if (val == null)
            return; // whoops
        Type t = val.GetType();
        string r = "";
        if (t.IsArray)
        {
            Type t_item = t.GetElementType();
            r += "Type=" + t_item.Name + "[], value=[";
            //
            // ??? OK, now what ? How to iterate through the elements of the array ???
            // Needs to work for arrays of simple type like Bool or arrays of objects...
            //
            r += "]";
        }
        else
        {
            r += "Type=" + t.Name + ", value=";
            r += val.ToString();
        }
        MessageBox.Show(r);
    }
5
  • Do you actually need reflection or just iteration through some objects? Commented Oct 13, 2013 at 21:50
  • Consider that the array will contain many items generally, how do you want to do with all of them? Your code seems to deal with a single value for the r variable Commented Oct 13, 2013 at 21:54
  • @ErikPhilips that answer is java, not c#. but it is basically an exact duplicate of stackoverflow.com/questions/104603/… Commented Oct 13, 2013 at 21:55
  • Still having problems. Answer I posted below is fine for reading the structure, but doesn't work for deserialization. What I need is WRITEABLE references to the objects or properties in the class, but "foreach" or "[n] subscripting" produce read-only references that cannot be used as "ref" arguments to a function that fills in the objects from the serial-data-store. Aaarrrgggg.... Commented Oct 14, 2013 at 21:04
  • @DaveNadler 1.Do not post part of question to the answer. No one will see it. You can use edit your question(edit button). 2. Your code works fine. Show us the problem code, so we can understand what you try to achieve and what is wrong. Commented Oct 17, 2013 at 15:15

4 Answers 4

8

The simplest way would be to just cast the object as IEnumerable, which arrays implement.

IEnumerable items = val as IEnumerable;
foreach (object o in items)
{
    r += ((o != null) ? o.ToString() : "") + ", ";
}
Sign up to request clarification or add additional context in comments.

2 Comments

Do you mean: IEnumerable<object> items = val as IEnumerable<object>; items is null for example for an array of Bool...
Nope, just plain IEnumerable
4

To iterate through an array you can use IEnumerable interface

IEnumerable array = val as IEnumerable;
if (array != null)
{
    foreach (var element in array)
    {
        MessageBox.Show(element.ToString());
    }
}

Every array implements IEnumerable.

1 Comment

@DaveNadler there is generic and non-generic IEnumerable interface msdn.microsoft.com/en-us/library/… msdn.microsoft.com/en-us/library/9eekhta0.aspx
2

In addition to the other answers, you can also use the GetValue of the Array class. All arrays inherit from Array so if IsArray is true you can cast in to Array

Note though this is really only needed if you want to adress multidimentional arrays. For most cases you can just cast to IEnumerable. You can even just to that for multidimentional arrays if you only want to enumerate all the values

Comments

0

Here's the answer as shipped. The important bits I initially missed are:

  • You cannot write to elements inside an IEnumerable (foreach),
  • forget about trying to cast to an array and use the [] operator (just use get/set value),
  • boxing is required sometimes in places you don't expect

Note this serializer works for FIXED SIZE classes (attribute classes are used to specify string lengths, target for deserialization must have default initializations for all components and no nulls). This is used where C# built-in serializers are too verbose for storing values into a space-limited embedded device's flash. It doesn't support multi-dimensional arrays (exercise for the reader).

Hope someone finds this helpful, thanks for the suggestions,

Best Regards, Dave

    private static void navelGaze(string name, int level, ref object val, int storageSize)
    {
        Debug.Assert(val != null); // can happen with an unitialized string (please init to "")
        Type t = val.GetType();
        StringBuilder diagString = new StringBuilder(100);
        diagString.Append(name); diagString.Append(": ");
        if (t.IsArray)
        {
            Type t_item = t.GetElementType();
            Array ar = val as Array;
            int len = ar.Length;
            diagString.Append("Type="); diagString.Append(t_item.Name);
            diagString.Append("["); diagString.Append(len); diagString.Append("]");
            if (t_item.BaseType.Name == "ValueType")
            {
                // array of primitive types like Bool or Int32...
                diagString.Append(", value=[");
                // Because C# does not permit modification of the iteration object
                // in "foreach", nor subscript operations on an array of primitive values,
                // use "GetValue/Setvalue" and a regular index iteration.
                for(int idx=0; idx<len; idx++) {
                    object arrayElementBoxObj = ar.GetValue(idx);
                    Munger(ref arrayElementBoxObj, 0);
                    diagString.Append(arrayElementBoxObj.ToString());
                    diagString.Append(", ");
                    if (currentOperation == SerializerOperation_T.Deserialize)
                        ar.SetValue(arrayElementBoxObj, idx);
                }
                diagString.Append("]");
                WriteDiagnostic(level, diagString.ToString());
                return;
            }
            else
            {
                // This is an array of a complex type; recurse for each element in the array...
                WriteDiagnostic(level, diagString.ToString());
                // The following cast operation is required to subscript 'ar'...
                // Note an array of a primitive type like 'int' cannot be cast to an array of objects.
                object[] vResObjArray = (object[])ar;
                for(int i=0; i<len; i++) {
                    object boxObj = vResObjArray[i];
                    navelGaze(name + "[" + i + "]", level + 1, ref boxObj, 0);
                    if (currentOperation == SerializerOperation_T.Deserialize)
                    {
                        // Setting vResObjArray[x] DOES set the original object passed into
                        // this function, as required for deserialization.
                        vResObjArray[i] = boxObj;
                    }
                }
                return;
            }
        }
        else if (t.Name == "String")
        {
            // 'String' is actually a class, but normal class handling below blows up...
            diagString.Append("Type="); diagString.Append(t.Name);
            diagString.Append(", value=");
            Munger(ref val, storageSize);
            diagString.Append(val.ToString());
        }
        else if (t.IsClass)
        {
            // Decompose class and recurse over members
            WriteDiagnostic(level, diagString + "Class " + t.Name);
            // Note that custom attributes are associated with the class's fields and properties,
            // NOT the type of the value of the fields/properties, so this must be checked here
            // prior recursion to process the values...
            // GetFields does not get the PROPERTIES of the object, that's very annoying...
            FieldInfo[] fi = val.GetType().GetFields();
            foreach (FieldInfo f in fi)
            {
                if (f.IsStatic) continue; // ignore class constants
                // Skip this if the FIELD is marked [HSB_GUI_SerializeSuppress]
                HSB_GUI_SerializeSuppressAttribute[] GUI_field_suppressSerializationAtt =
                    (HSB_GUI_SerializeSuppressAttribute[])
                    f.GetCustomAttributes(typeof(HSB_GUI_SerializeSuppressAttribute), true);
                if (GUI_field_suppressSerializationAtt.Length > 0)
                    continue; // this field is marked with "suppress serialization to GUI save are"
                // Get optional size specifier (required for strings)
                int nextLevelStorageSize = 0;
                HSB_GUI_SerializableAttribute[] HSB_GUI_SerializableAtt =
                    (HSB_GUI_SerializableAttribute[])
                    f.GetCustomAttributes(typeof(HSB_GUI_SerializableAttribute), true);
                if (HSB_GUI_SerializableAtt.Length > 0)
                    nextLevelStorageSize = HSB_GUI_SerializableAtt[0].StorageLength;
                // box, and gaze into this field...
                object boxObj = f.GetValue(val);
                // could replace null with default object constructed here
                navelGaze(f.Name, level + 1, ref boxObj, nextLevelStorageSize);
                if (currentOperation == SerializerOperation_T.Deserialize)
                    f.SetValue(val, boxObj);
            }
            // Now iterate over the PROPERTIES
            foreach (PropertyInfo prop in /*typeof(T)*/ val.GetType().GetProperties())
            {
                // Skip this if the PROPERTY is marked [HSB_GUI_SerializeSuppress]
                HSB_GUI_SerializeSuppressAttribute[] GUI_prop_suppressSerializationAtt =
                    (HSB_GUI_SerializeSuppressAttribute[])
                    prop.GetCustomAttributes(typeof(HSB_GUI_SerializeSuppressAttribute), true);
                if (GUI_prop_suppressSerializationAtt.Length > 0)
                    continue; // this property is marked with "suppress serialization to GUI save are"
                // not relevant; does not occur: if (!type.IsSerializable) continue;
                // Get optional size specifier (required for strings)
                int nextLevelStorageSize = 0;
                HSB_GUI_SerializableAttribute[] HSB_GUI_SerializableAtt =
                    (HSB_GUI_SerializableAttribute[])
                    prop.GetCustomAttributes(typeof(HSB_GUI_SerializableAttribute), true);
                if (HSB_GUI_SerializableAtt.Length > 0)
                    nextLevelStorageSize = HSB_GUI_SerializableAtt[0].StorageLength;
                // box, and gaze into this field...
                object boxObj = prop.GetValue(val, null);
                // could replace null with default object constructed here
                navelGaze("Property " + prop.Name, level + 1, ref boxObj, nextLevelStorageSize);
                if (currentOperation == SerializerOperation_T.Deserialize)
                    prop.SetValue(val, boxObj, null);
            }
            return;
        }
        else if (t.IsEnum)
        {
            Munger(ref val, 0);
            diagString.Append("Enum ("); diagString.Append(t.Name); diagString.Append("), value=");
            diagString.Append(val.ToString()); diagString.Append(", raw value="); diagString.Append((int)val);
        }
        else
        {
            Munger(ref val, 0);
            diagString.Append("Type="); diagString.Append(t.Name);
            diagString.Append(", value="); diagString.Append(val.ToString());
        }
        WriteDiagnostic(level,diagString.ToString());
    }

    public static HSB_Settings_Class DeserializeResult(byte[] datastream)
    {
        Debug.Assert(datastream.Length == 0x0600);
        idx = 0;
        buf = datastream;
        currentOperation = SerializerOperation_T.Deserialize;
        HSB_Settings_Class new_HSB_settings = new HSB_Settings_Class();
        object boxObj = new_HSB_settings;
        navelGaze("DeserializeResult", 0, ref boxObj, 0);
        new_HSB_settings = (HSB_Settings_Class)boxObj;
        Console.WriteLine("==== Deserialization used a total of " + idx.ToString() + " bytes (out of 1536 available) ====");
        return new_HSB_settings;
    }

    public static byte[] SerializeHSBsettings(ref HSB_Settings_Class hsbset)
    {
        idx = 0;
        currentOperation = SerializerOperation_T.Serialize;
        object boxObj = hsbset;
        navelGaze("SerializeHSB", 0, ref boxObj, 0);
        Console.WriteLine("==== Serialization used a total of "+idx.ToString() + " bytes (out of 1536 available) ====");
        return buf;
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.