Christian Ruppert
Christian Ruppert

Reputation: 3779

Generic collections - check if class property is one

I am kind of stuck with a simple type check on a generic helper class I am writing. The idea will be clear if you take a look at the source code. It works nice for normal things, like DateTime and bool, but I got a problem with generics.

How do I check for e.g. ObservableCollection < something > and count it. I do not want to be able to render everything (its a debug control after all), but if there is a collection, I want to know if it has elements.

Tried some variations, but none worked. Can't be that hard, the search words just suck (generic, list, collection) - all not very google friendly :-)

[EDIT]
Ok, I played around a little bit, and I get closer. There is however a strange problem with the GetValue() function. If you take a look at the example class, the one string property is working fine, the one set in the constructor shows "NULL" - how can that be. Oh, btw. I am working in Windows Phone 7 - but this should not affect such basic stuff, should it?
[END EDIT]

Updated version of my function:

        private static FrameworkElement CreateContent(System.Reflection.PropertyInfo propertyInfo, object parent)
    {

        var propValue = propertyInfo.GetValue(parent, null);

        // NULL

        if (propValue == null) return new TextBlock() { Text = "NULL" };

        // Simple cases

        if (propertyInfo.PropertyType == typeof(int))
            return new TextBlock() { Text = propValue.ToString() };

        if (propertyInfo.PropertyType == typeof(DateTime))
            return new TextBlock() { Text = ((DateTime)propValue).ToShortTimeString() };

        if (propertyInfo.PropertyType == typeof(string))
            return new TextBlock() { Text = propValue.ToString() };

        // More exotic cases

        if (typeof(IList).IsAssignableFrom(propValue.GetType()))
                return new TextBlock() {Text = ((IList) propValue).Count.ToString()};

        if (typeof(ICollection).IsAssignableFrom(propValue.GetType()))
            return new TextBlock() { Text = ((ICollection)propValue).Count.ToString() };

        if (typeof(IEnumerable).IsAssignableFrom(propValue.GetType()))
            return new TextBlock() { Text = ((IEnumerable)propValue).Cast<object>().Count().ToString() };

        // If all fails, at least tell me the type

        return new TextBlock() {Text = propertyInfo.PropertyType.Name};
    }

Very very strange test case (look for StringPropertyA and B)

 public class DebugService : ServiceBase, IDebugService
{
    public DebugService(IUnityContainerPhone container) : base(container)
    {
        DateTimeProperty = DateTime.Now;
        StringEmptyProperty = "";
        StringObsColl = new ObservableCollection<string>() {"A", "B", "C"};
        StringList = new List<string>(){"X", "Y", "Z"};

        StringPropertyB = "Will not show up";

    }

    public override string Description
    {
        get { return "Does nothing, for debug purposes"; }
    }

    public DateTime DateTimeProperty { get; private set; }
    public int IntProperty { get { return 23; } }
    public string StringPropertyA { get {return  "Is OK";}  }
    public string StringPropertyB { get; private set; }
    public string StringEmptyProperty { get; private set; }
    public string StringNullProperty { get; private set; }
    public ObservableCollection<string> StringObsColl { get; private set; }
    public IList<string> StringList { get; private set; }
}

Upvotes: 0

Views: 2352

Answers (3)

David Yaw
David Yaw

Reputation: 27864

I think you have your IsAssignablefrom method call backwards. I think you need to say typeof(IList).IsAssignableFrom(propertyInfo.PropertyType)

Regardless, I would retrieve the object directly and reflect on it, rather than reflecting on the type of the property. This way, if you have a property of type object, that returns a list, it will display properly in your debug control.

private static FrameworkElement CreateContent(System.Reflection.PropertyInfo propertyInfo, object parent)
{
    object propValue = propertyInfo.GetValue(parent, null);

    if (propValue is ICollection)
        return new TextBlock() { Text = ((ICollection)propValue).Count.ToString() };
    else if (propValue is ...)
        return new TextBlock() { Text = ... };
    else
        return new TextBlock() { Text = propValue.ToString() };
}

Upvotes: 0

Dan Bryant
Dan Bryant

Reputation: 27495

propertyInfo.PropertyType.IsAssignableFrom(typeof(IList))

This is a classic mistake (I think it's so common because of the value is IList syntax). What you actually want is:

typeof(IList).IsAssignableFrom(propertyInfo.PropertyType)

There are other issues to consider, such as whether you want to support other types of collections (not all collections will implement IList.) If you're specifically interested in getting access to the value as a generic collection, a helpful hint is that you can use typeof(IList<>) to get the non-specialized generic type.

Upvotes: 1

Erik Noren
Erik Noren

Reputation: 4339

Is it necessary to go this route?

if (parent is DateTime)
   return new TextBlock() { Text = ((DateTime)parent).ToShortTimeString() };

if (parent is ICollection)
   return new TextBlock() { Text = ((ICollection)parent).Count };

if (parent is IEnumerable)
   return new TextBlock() { Text = ((IEnumerable)parent).Count() }; //assumes System.Linq

...

return new TextBlock() { Text = parent.ToString() };

Upvotes: 1

Related Questions