【问题标题】:Howto search through Properties of all kinds of types如何搜索各种类型的属性
【发布时间】:2015-10-06 16:01:05
【问题描述】:

我有一个名为Part 的基类和WireConnector 等派生类,还有更多继承自Part 的类。

现在我想实现一个搜索函数,在派生类的所有属性中搜索字符串。

如有必要,应尝试将该字符串转换为属性的类型。属性也可以是列表,应该在第一级搜索。

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

我知道如何通过这样的反射来搜索基本类型的属性

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

但这将是一长串类型,例如,我有类型为 Weight 的属性等等。是否有某种通用的搜索方式而不需要转换所有类型?

【问题讨论】:

  • 可能有点笨拙,但我认为像IStringSearchable 这样的非原始类型的接口定义会有所帮助。可能有一个更好的解决方案,但不需要触及每个班级。
  • 如果被检查的对象包含复杂类型的属性,你的意思是也要检查它的属性吗?我问是因为在问题中您使用的是“派生类的所有属性”这一短语。
  • 派生类是指从Part继承的所有类。但是对于复杂类型,最好检查一下它的所有属性,但仅限于此。

标签: c# search reflection


【解决方案1】:

考虑与您的转化相反。无需将搜索字符串转换为每个可能的值,只需将值转换为字符串即可:

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

除了对大多数内置类型工作得很好之外,要让它对Weight 等工作,您唯一需要做的就是确保它们实现了ToString()

另一种解决方案是使用 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 适用于大多数内置类型,但requires extra work 如果您正在处理自定义类型。

【讨论】:

  • @OnlyaCuriousMind:如果他们实现了ToString()
  • 但是值可以有不同的字符串表示形式,所以 IMO 这条路线并不等同于 OP 要求的行为。
  • @IvanStoev:同意。我用另一种可能性更新了我的答案。两者都有优点和缺点,但ToString() 方法更简单,所以我从那个开始。
【解决方案2】:

我认为以下应该涵盖大部分实际场景:

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

【讨论】:

    【解决方案3】:

    我会给你一个不同的想法。

    你可以试试这样的:

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

    你需要这样调用方法:

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

    如果您需要通过与GetHashCode 不同的方式比较复杂属性(重量,...),您可以覆盖方法Equals== 运算符。

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

    【讨论】:

    • 这不满足要求:一个给定的搜索字符串应该搜索对象上的所有属性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多