juergen d
juergen d

Reputation: 204766

Howto search through Properties of all kinds of types

I have a base class called Part and derived classes like Wire or Connector and many more that inherit from Part.

Now I want to implement a search function that searches all Properties of the derived classes for a string.

If necessary that string should be tried to be converted to the type of the Property. The Properties can also be Lists and should be searched on the first level.

class Part 
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class Wire : Part
{
    public NumberWithUnit Diameter { get; set; }
    public Weight Weight { get; set; }
}

class Connector : Part
{
    public List<Part> ConnectedParts { get; set; }
}

I know how to generally search through the Properties of base types with Reflection like this

private bool SearchProperties<T>(T part, string searchString) where T : Part
{
    var props = typeof(T).GetProperties();
    foreach (var prop in props)
    {
        var value = prop.GetValue(part);
        if (value is string)
        {
            if (string.Equals(value, searchString))
                return true;
        }
        else if (value is int)
        {
            int v;
            if (int.TryParse(searchString, out v))
            {
                if(v == (int) value)
                    return true;
            }
        }
    }
    return false;
}

But that would be a long list of types and I have Properties of Type Weight for instance and many more. Is there some kind of general way to search without casting all types?

Upvotes: 3

Views: 284

Answers (3)

Ivan Stoev
Ivan Stoev

Reputation: 205629

I think the following should cover the most of the practical scenarios:

public static bool SearchProperties(object target, string searchString)
{
    if (target == null) return false;
    // Common types
    var convertible = target as IConvertible;
    if (convertible != null)
    {
        var typeCode = convertible.GetTypeCode();
        if (typeCode == TypeCode.String) return target.ToString() == searchString;
        if (typeCode == TypeCode.DBNull) return false;
        if (typeCode != TypeCode.Object)
        {
            try
            {
                var value = Convert.ChangeType(searchString, typeCode);
                return target.Equals(value);
            }
            catch { return false; }
        }
    }
    if (target is DateTimeOffset)
    {
        DateTimeOffset value;
        return DateTimeOffset.TryParse(searchString, out value) && value == (DateTimeOffset)target;
    }
    var enumerable = target as IEnumerable;
    if (enumerable != null)
    {
        // Collection
        foreach (var item in enumerable)
            if (SearchProperties(item, searchString)) return true;
    }
    else
    {
        // Complex type
        var properties = target.GetType().GetProperties();
        foreach (var property in properties)
        {
            if (property.GetMethod == null || property.GetMethod.GetParameters().Length > 0) continue;
            var value = property.GetValue(target);
            if (SearchProperties(value, searchString)) return true;
        }
    }
    return false;
}

Upvotes: 1

StriplingWarrior
StriplingWarrior

Reputation: 156524

Consider going the opposite direction with your conversion. Rather than converting your search string into each possible value, just convert the value into a string:

private bool SearchProperties<T>(T part, string searchString) where T : Part
{
    var props = typeof(T).GetProperties();
    foreach (var prop in props)
    {
        var value = prop.GetValue(part);
        if (value is IEnumerable)
        {
            // special handling for collections
        }
        else if(value != null)
        {
            string valueString = value.ToString();
            if (string.Equals(valueString, searchString))
                return true;
        }
    }
    return false;
}

Besides working pretty well for most built-in types, the only thing you have to do to get it to work for Weight, etc. is make sure they implement ToString().

Another solution would be to use TypeDescriptor:

private bool SearchProperties<T>(T part, string searchString) where T : Part
{
    var props = typeof(T).GetProperties();
    foreach (var prop in props)
    {
        var value = prop.GetValue(part);
        if (value is IEnumerable)
        {
            // special handling for collections
        }
        else if(value != null)
        {
            object searchValue = null;
            try
            {
                searchValue = TypeDescriptor.GetConverter(value).ConvertFromString(searchString);
            } catch {}
            if (searchValue != null && object.Equals(value, searchValue))
                return true;
        }
    }
    return false;
}

TypeDescriptor works well for most built-in types, but requires extra work if you're dealing with custom types.

Upvotes: 2

Only a Curious Mind
Only a Curious Mind

Reputation: 2857

I will give you one different idea to do it.

You could try something like that:

 private bool SearchProperties<T, W>(T part, W searchValue) where T : Part
   {
       var props = typeof(T).GetProperties();
       foreach (var prop in props)
       {
           if (typeof(W) == prop.PropertyType)
           {
               var value = prop.GetValue(part, null);

               if (searchValue.Equals(value))
                   return true;
           }
       }
       return false;
   }

You need to call the method like this:

    private void button12_Click(object sender, EventArgs e)
    {
        Part p = new Part();
        p.Id = 2;
        p.Name = "test";
        p.bla = new Bla();

        SearchProperties<Part, int>(p, 2);
    }    

And if you need to compare the complex properties (Weight, ...) by a different way from GetHashCode you could override the method Equals or the == operator.

   class Weight
   {
       public int Id { get; set; }

       public override bool Equals(object obj)
       {
           return Id == ((Weight)obj).Id;
       }
   }

Upvotes: 0

Related Questions