Reputation: 101
This is My Class which is basically Closed Coupled To Type I want to Generalise this Class.
So I can use with all the Class
one thing is common that there will be a Name field while invoking this method
public class CustomSuggestionProvider : ISuggestionProvider
{
private const int batchSize = 30;
private string _criteria = string.Empty;
private int _skipCount;
private readonly ObservableCollection<LanguageItem> _observableCollection;
private readonly List<LanguageItem> _source;
public CustomSuggestionProvider(ObservableCollection<LanguageItem> observableCollection, List<LanguageItem> source)
{
_observableCollection = observableCollection;
_source = source;
}
public bool HasMoreSuggestions { get; private set; } = true;
public Task<IList<object>> GetSuggestionsAsync(string criteria, CancellationToken cancellationToken)
{
_criteria = criteria;
var newItems = _source.Where(x => x.Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
if (cancellationToken.IsCancellationRequested)
return null;
HasMoreSuggestions = newItems.Count > batchSize;
_skipCount = batchSize;
return Task.FromResult<IList<object>>(newItems.Take(batchSize).Cast<object>().ToList());
}
}
I Just made it Generalised like this.
Made all the closely coupled class to Type T.
Kindly have a look.
public class CustomSuggestionProvider<T> : ISuggestionProvider
{
private const int batchSize = 30;
private string _criteria = string.Empty;
private int _skipCount;
private readonly ObservableCollection<T> _observableCollection;
private readonly List<T> _source;
public CustomSuggestionProvider(ObservableCollection<T> observableCollection, List<T> source)
{
_observableCollection = observableCollection;
_source = source;
}
public bool HasMoreSuggestions { get; private set; } = true;
]
public Task<IList<object>> GetSuggestionsAsync(string criteria, CancellationToken cancellationToken)
{
_criteria = criteria;
var newItems = _source.Where(x =>Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
if (cancellationToken.IsCancellationRequested)
return null;
HasMoreSuggestions = newItems.Count > batchSize;
_skipCount = batchSize;
return Task.FromResult<IList<object>>(newItems.Take(batchSize).Cast<object>().ToList());
}
public Task<IList<object>> GetSuggestionsAsync(CancellationToken cancellationToken)
{
var newItems = _source.Where(x => x.Name.StartsWith(_criteria)).Skip(_skipCount).ToList();
if (cancellationToken.IsCancellationRequested)
return null;
HasMoreSuggestions = newItems.Count > batchSize;
_skipCount += batchSize;
return Task.FromResult<IList<object>>(newItems.Take(batchSize).Where(x => !_observableCollection.Any(y => y.Id == x.Id)).Cast<object>().ToList());
}
}
I just stuck when at this statement
The 'Name' does not exist here
var newItems = _source.Where(x => x.Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
Can you please help troubleshoot this.
Thanks in Advance.
Upvotes: 0
Views: 128
Reputation: 348
You sould to write an interface like this
public interface IName
{
string Name { set; get; }
}
note : all you'r classes should implement from IName
write function like this
public static IList<IName> Names(IList<IName> names, string _criteria)
{
return names.Where(c => c.Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
}
and this for IEnumerable as extension method
public static class IEnumerableExtension
{
public static IEnumerable<IName> Filter(this IEnumerable<IName> names, string _criteria)
{
return names.Where(c => c.Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0);
}
}
Upvotes: 2
Reputation: 63398
Nicer (in my opinion) than either the 'require an IName
interface' or 'use reflection' options is this: when constructing a CustomSuggestionProvider<T>
, pass in a Func<T, string>
which the provider can invoke whenever it needs a T
's name.
Add in CustomSuggestionProvider<T>
Func<T, string> nameGetter;
Initialise it in the constructor, with callers passing in something like class => class.Name
.
Then whenever you need a T
's name, you just invoke this function. So your
var newItems = _source
.Where(x => x.Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0)
.ToList();
becomes
var newItems = _source
.Where(x => nameGetter(x).IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0)
.ToList();
Upvotes: 0
Reputation: 2216
You has two options. First - object must inherit interface/class that contains Name property. Second you can use reflection and access the Name property with compiled lambda
List<ObjectWithName> items = new List<ObjectWithName>()
{
new ObjectWithName { Name = "Hello World #1"},
new ObjectWithName { Name = "Hello World #2"}
};
var result = FirstOption(items, "Hello");
result = SecondOption(items, "Hello");
...
public static List<T> FirstOption<T>(IList<T> source, string criteria) where T: IObjectWithName
{
return source.Where(t => t.Name.IndexOf(criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
}
public static List<T> SecondOption<T>(IList<T> source, string criteria)
{
PropertyInfo propertyInfo = typeof(T).GetProperty("Name");
MethodInfo getterMethodInfo = propertyInfo.GetGetMethod();
ParameterExpression entity = Expression.Parameter(typeof(T));
MethodCallExpression getterCall = Expression.Call(entity, getterMethodInfo);
UnaryExpression castToObject = Expression.Convert(getterCall, typeof(string));
LambdaExpression lambda = Expression.Lambda(castToObject, entity);
var functionThatGetsValue = (Func<T, string>)lambda.Compile();
return source.Where(t => functionThatGetsValue(t).IndexOf(criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
}
}
public class ObjectWithName: IObjectWithName
{
public string Name { get; set; }
}
public interface IObjectWithName
{
string Name { get; }
}
For optimization you can compile lambda only once for type and cache results
Upvotes: 2