1

.Net 4.5 C#

To support an "Export to Excel" function for many classes, I need to be able to take a class, then from it get a list of the public property names (for the header) and the property values (for the rows)

Is there a way to reflect on an instance of a class and get the Property names and property values?

Charles
  • 50,943
  • 13
  • 104
  • 142
Ian Vink
  • 66,960
  • 104
  • 341
  • 555

3 Answers3

1

Here's a quick sample on how you can do this. Of course, you will need to update it so that you create/construct the CSV correctly, but this gives you an idea on how you can get the values you need.

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass()
        {
            Number = 1,
            String = "test"
        };

        var properties = myClass.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (var property in properties)
        {
            var columnName = property.Name;
            var value = myClass.GetType().GetProperty(columnName).GetValue(myClass, null);

            Console.WriteLine(string.Format("{0} - {1}", columnName, value));
        }
        Console.ReadKey();
    }
}

public class MyClass
{
    public int Number { get; set; }
    public string String { get; set; }
}
d.moncada
  • 16,900
  • 5
  • 53
  • 82
1

I actually recently created something like this for my job. I was writing a custom CSV serializer class to support an ill-conceived CSV file specification.

public CSVSerializer Serialize<T>(T data, Func<T, object> map)
{
    if (map == null) throw new ArgumentNullException("map");

    object mappedData = map(data);
    if (mappedData == null) throw new NullReferenceException("Mapped data produced null value");

    // Iterate over public members of `mappedData`
    MemberInfo[] members = mappedData.GetType().GetMembers(BindingFlags.Instance | BindingFlags.Public);
    List<string> values = new List<string>();
    foreach (MemberInfo member in members)
    {
        // Skip events and methods
        if (!(member is FieldInfo || member is PropertyInfo)) continue;

        // Value of `mappedData`
        object memberVal = MemberInfoValue(member, mappedData);
        if (memberVal == null)
        {
            // If the actual value stored by `memberVal` is null, store string.Empty and continue
            values.Add(string.Empty);
            continue;
        }

        // Check if `memberVal` contains a member named "map"
        MemberInfo[] maps = memberVal.GetType().GetMember("map");
        MemberInfo memberMap = maps.Length > 0 ? maps[0] : null;
        string val = MapToString(memberVal, o => o.ToString());

        if (map != null) // map is present
        {
            // Get first property other than map
            MemberInfo dataVal = memberVal.GetType().GetMembers(BindingFlags.Instance | BindingFlags.Public)
                                                .Where(mi => mi is FieldInfo || mi is PropertyInfo)
                                                .Except(new MemberInfo[] { memberMap })
                                                .DefaultIfEmpty(memberMap)
                                                .FirstOrDefault();

            object tmp = MemberInfoValue(memberMap, memberVal);
            if (dataVal == memberMap)
            {
                // map is only property, so serialize it
                val = MapToString(tmp, o => o.ToString());
            }
            else
            {
                // try to serialize map(dataVal), or use empty string if it fails
                Delegate dlg = tmp as Delegate;
                if (dlg != null)
                {
                    object param = MemberInfoValue(dataVal, memberVal);
                    try { val = MapToString(dlg, d => d.DynamicInvoke(param).ToString()); }
                    catch (Exception ex)
                    {
                        // exception should only occur with parameter count/type mismatch
                        throw new SerializationException(string.Format("Poorly formatted map function in {0}", member.Name), ex);
                    }
                }
                else
                {
                    // map is not a delegate (!!)
                    throw new SerializationException(string.Format("map member in {0} is not a delegate type", member.Name));
                }
            }
        }

        // Handle quotes and the separator string
        val = val.Trim('"');
        if (val.Contains("\""))
        {
            val = val.Replace("\"", "\\\"");
        }
        if (val.Contains(Separator))
        {
            val = string.Format("\"{0}\"", val);
        }

        values.Add(val);
    }
    string line = string.Join(Separator, values);
    Writer.WriteLine(line);

    return this;
}

This function serializes the public fields and properties of data in the order they're defined; the names of those members are ignored.

If you're only interested in properties, you can use GetProperties instead of GetMembers (using PropertyInfo instead of MemberInfo). If you're not creating a CSV, you can obviously ignore the CSV file formatting portion.

MemberInfoValue(info, parent) returns ((FieldInfo)info).GetValue(parent) or ((PropertyInfo)info).GetValue(parent, null), as appropriate. MapToString is just a null-guard function.

The presence of the map parameter and hunting for a member named "map" is part of the necessities of my particular situation, although it may be useful to you. The mapping allows for things such as:

mySerializer.Serialize(myPersonObject,
    p => new {
        name = string.Format("{0}, {1}", p.LastName, p.FirstName),
        address = p.address
    });

I have an overload of Serialize which calls Serialize(data, d => d);. Hunting for the "map" member allows things like:

mySerializer.Serialize(new {
        employeeID = new { val = myEmployeeObject.ID, map = d => d.ToUpperCase() },
        employeeName = myEmployeeObject.Name
    });
Brian S
  • 4,878
  • 4
  • 27
  • 46
0

You could use some like this:

public static Dictionary<string, object> KeyValue(object obj)
{
    return obj.GetType().GetProperties().ToDictionary(
        m => m.Name, 
        m => m.GetValue(obj, new object[] { })
    );
}
iuristona
  • 927
  • 13
  • 30