patterncsharpMinor
Implementing a Property Searcher
Viewed 0 times
propertyimplementingsearcher
Problem
I want to search some or all public properties of a class and for this requirement I have implemented a
The Input for a
This is what i have implemented:
SearchResult.cs
ISearchable.cs
Extension Methods for easier usage:
Extension.cs
I also want to show you the example and test code for e.g. searching in the property of a
Property Searcher. The Input for a
Search is always a string. The Output contains the SearchString, the SearchContent in which the SearchString is contained and the searched Property.The Output is wrapped in a SearchResult object.This is what i have implemented:
SearchResult.cs
public class SearchResult
{
public PropertyInfo Property { get; private set; }
public string SearchString { get; private set; }
public string SearchContent { get; private set; }
public ISearchable Item { get; private set; }
public SearchResult(ISearchable item,
PropertyInfo property,
string searchString)
{
this.Property = property;
this.SearchString = searchString;
this.Item = item;
this.SearchContent = property.GetValue(item).ToString();
}
}ISearchable.cs
public interface ISearchable
{
IEnumerable Search(string searchString);
}Extension Methods for easier usage:
Extension.cs
public static class Extension
{
public static SearchResult Search(this ISearchable source, string searchString, PropertyInfo info)
{
if (info.GetValue(source).ToString().Contains(searchString, StringComparison.OrdinalIgnoreCase))
{
return new SearchResult(source, info, searchString);
}
return null;
}
public static IEnumerable Search(this IEnumerable source, string searchString)
{
return source.SelectMany(searchable => searchable.Search(searchString)).Where(x => x != null);
}
public static bool Contains(this string source, string toCheck, StringComparison comp)
{
return source.IndexOf(toCheck, comp) >= 0;
}
}I also want to show you the example and test code for e.g. searching in the property of a
Book with a Description (the code is simplified and mSolution
I don't have a C# compiler at hand and cannot test the following.
It looks like the
The method will likely always consist of a list of these types of code blocks:
-
the delegate type
-
for the direct search
-
no code for excluded properties
This smells like copy&paste. But you are already doing reflection, so why not use it to your advantage?
I think you can create a generic
1 The minority thing is only to reduce future work, minimizing the amount of attributes that you have to add. Attributes are optional, so you have to define some default behaviour anyway.
Let's say the default behaviour is to search all properties and opt-out via attribute. Here's how your classes could look like:
As you can see in the
The generic extension method would perform the following two steps then:
Some additional thoughts:
The attribute makes the search independent from the actual names of the properties. If you change
If you wish to perform the searching/delegating in a special order, you could do that, too. For example via a property of another attribute. See
It looks like the
Search() always returns the sum of results of these 3 things:- delegate the call to any property that's
ISearchable.
- search the value of some properties
- do not search the value of some other properties
The method will likely always consist of a list of these types of code blocks:
-
the delegate type
foreach (var searchResult in this.PROPERTY_THAT_IS_ISEARCHABLE.Search(searchString))
{
yield return searchResult;
}-
for the direct search
yield return this.Search(searchString, this.GetType().GetProperty(nameof(this.PROPERTY_THATS_NOT_ISEARCHABLE)));-
no code for excluded properties
This smells like copy&paste. But you are already doing reflection, so why not use it to your advantage?
I think you can create a generic
Search() method, then slap that onto everything that implements ISearchable via extension, getting you rid of explicitely implementing the method in those classes. Here's how:- You should be able to filter out all properties that implement
ISearchableand delegate the method call to them.
- For non-
ISearchableproperties that should be searched, you should decide if they are the minority1. If so, define an attribute to opt-in a property. You'd then ignore all non-ISearchableby default and only search the values of those that are explicitly opted-in with the attribute.
- For non-
ISearchableproperties that should not be searched, do pretty much the opposite of (2.). If they are the minority, create and add to them an attribute explicitly to opt-out of the default which would be to search all non-ISearchableproperties.
1 The minority thing is only to reduce future work, minimizing the amount of attributes that you have to add. Attributes are optional, so you have to define some default behaviour anyway.
Let's say the default behaviour is to search all properties and opt-out via attribute. Here's how your classes could look like:
public class Book : ISearchable
{
public DescriptionInfo DescriptionInfo { get; set; }
public string Title { get; set; }
}As you can see in the
Book class: ISearchable is more or less a marker interface.public class DescriptionInfo : ISearchable
{
public string Description { get; set; }
[IgnoreThisDuringSearch]
public string ShortDescription { get; private set; }
public DescriptionInfo(string description)
{
if (!string.IsNullOrEmpty(description))
{
this.Description = this.ShortDescription = description;
}
}
}The generic extension method would perform the following two steps then:
- delegate to every property that it can be delegated to
- search every property that got opt-in via attribute or search every property except those opt-out via attribute
Some additional thoughts:
The attribute makes the search independent from the actual names of the properties. If you change
Title to Totle in the Book class, you current code breaks. The suggested solution is solely based on reflection and existance (lack of) an attribute, which should always work, given a sensible default behaviour.If you wish to perform the searching/delegating in a special order, you could do that, too. For example via a property of another attribute. See
[DataMember(Order = n)] for example.Code Snippets
foreach (var searchResult in this.PROPERTY_THAT_IS_ISEARCHABLE.Search(searchString))
{
yield return searchResult;
}yield return this.Search(searchString, this.GetType().GetProperty(nameof(this.PROPERTY_THATS_NOT_ISEARCHABLE)));public class Book : ISearchable
{
public DescriptionInfo DescriptionInfo { get; set; }
public string Title { get; set; }
}public class DescriptionInfo : ISearchable
{
public string Description { get; set; }
[IgnoreThisDuringSearch]
public string ShortDescription { get; private set; }
public DescriptionInfo(string description)
{
if (!string.IsNullOrEmpty(description))
{
this.Description = this.ShortDescription = description;
}
}
}Context
StackExchange Code Review Q#119638, answer score: 2
Revisions (0)
No revisions yet.