Dynamically checking IList<T> - c#

I have a class file named ParentPropertyAttribute with a Type Property as below:
public Type PropertyType { get; set; }
In one of my class, I need to do some work based on the type passed.
Right now, I am using if else condition as below:
if (parentPropertyAttribute.PropertyType == typeof(string))
{
return (parentList as IList<string>).Select(item => new SelectItem() { Value = item, Text = item }).OrderBy(selectItem => selectItem.Text);
}
else if (parentPropertyAttribute.PropertyType == typeof(XYZ))
{
return (parentList as IList<XYZ>).Select(x=> new SelectItem() { Value = item, Text = item }).OrderBy(selectItem => selectItem.Text);
}
The issue with the above is in future if there is any other type the if else case gets on increasing.
Is there an optimized way to dynamically assign the type (string/XYZ in this case) to achieve this.
Any input is appreciated.
Regards.

Since the only thing you're using with the IList<> interface is coming from the IEnumerable<> interface, there's a chance you could just do something like this:
return ((IEnumerable)parentList).Cast<object>()
.Select(item => new SelectItem { Value = item, Text = item.ToString() })
.OrderBy(selectItem => selectItem.Text)
.ToList();
This depends on all the valid types in your lists having ToString() implementations that are appropriate for this purpose, though.
Note that the ((IEnumerable)parentList).Cast<object>() could be simplified to ((IEnumerable<object>)parentList) if you know none of the types are Value Types (which need boxing to convert into objects).

Of course you can. You just need more generics in your code.
Please refer to this example:
class SmartListItem
{
public string Name { get; set; }
}
class SmartList
{
private System.Collections.IList list;
public SmartList(System.Collections.IList list)
{
this.list = list;
}
public List<T> FilterPropertyType<T>(Func<T, bool> query)
{
return list.Cast<T>().Where(query).ToList();
}
}
You can use this "SmartList" in different ways (without use ParentType because is not necessary):
var stringList = new SmartList(new[] { "Rob", "Liam" });
var matches = stringList.FilterPropertyType<string>(s => s.Contains("Rob"));
Or better with classes:
var classList = new SmartList(new []{
new SmartListItem { Name = "Rob" },
new SmartListItem { Name = "Liam" }
});
var matches = classList.FilterPropertyType<SmartListItem>(smi => smi.Name.Contains("Rob"));
Using that you can avoid switch case for PropertyType and do your queries directly on the list item.

Related

Generic FilterClass<T> - within the class a List<predicate>(T) shall be set up, how with unknown type at runtime?

I've built a complex filter for my ICollection within my ViewModel. Now I need a similar filterfunction for a different collection and datagrid. So I guess it would suck big times if I was going to duplicate and adjust my code.
So I was going for a reusable solution.
Simple Code:
public class FilterForGrid<T>
{
public T UiModel { get; set; }
private List<Predicate<T>> criteria = new List<Predicate<T>>();
public FilterForGrid() {
// var result = typeof(T).GetProperties().ToList();
}
private bool dynamicFilter(object obj) {
T uiModel = (T)obj;
bool isIn = true;
if (criteria.Count() == 0)
return isIn;
isIn = criteria.TrueForAll(x => x(uiModel));
return isIn;
}
public void ClearFilter() {
criteria.Clear();
}
public void AddFilterArgument(string argument, string property) {
// criteria.Add(new Predicate<T>(x => x.))
}
public void FireFilter(ICollectionView toBeFilteredCollection) {
toBeFilteredCollection.Filter = dynamicFilter;
toBeFilteredCollection.Refresh();
}
}
Have a look at the method "AddFilterArgument" --> I simply want to pass the name of the property and the value over which the data shall be filtered:
public void AddFilterArgument(string argument, string property) {
criteria.Add(new Predicate<T>(x => x.property == argument))
}
But because of type inteference the property can't be found this way.
Is my attemp possible or do I have to look in another direction? If its possible please give me a clue.
Well, finally it was a much easier than expected:
Example for one of the methods
public void AddFilterPredicate(string argument, string property, OperatorsForFIlter operators) {
Predicate<T> predicate;
if (!String.IsNullOrEmpty(argument)) {
switch (operators) {
case OperatorsForFIlter.equal:
predicate = new Predicate<T>(x => x.GetType().GetProperty(property).GetValue(x, null).ToString() == argument);
break;
case OperatorsForFIlter.contains:
predicate = new Predicate<T>(x => x.GetType().GetProperty(property).GetValue(x, null).ToString().Contains(argument));
break;
default:
predicate = null;
break;
}
} else { predicate = new Predicate<T>(x => x.GetType().GetProperty(property).GetValue(x, null).ToString().Contains(argument)); }
InsertIntoCriteriaCatalogue(property, predicate);
}
This line here was exactly what I was asking for:
new Predicate<T>(x => x.GetType().GetProperty(property).GetValue(x, null).ToString().Contains(argument));
I was looking for a way to pass a name of a property as parameter and the value of the property through which the list should be filtered.
Now I can use the dynamic generic filter for all my data in every grid.

LINQ select property by name [duplicate]

This question already has answers here:
Dynamic LINQ OrderBy on IEnumerable<T> / IQueryable<T>
(24 answers)
Closed 2 years ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
I'm attempting to use a variable inside of a LINQ select statement.
Here is an example of what I'm doing now.
using System;
using System.Collections.Generic;
using System.Linq;
using Faker;
namespace ConsoleTesting
{
internal class Program
{
private static void Main(string[] args)
{
List<Person> listOfPersons = new List<Person>
{
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person(),
new Person()
};
var firstNames = Person.GetListOfAFirstNames(listOfPersons);
foreach (var item in listOfPersons)
{
Console.WriteLine(item);
}
Console.WriteLine();
Console.ReadKey();
}
public class Person
{
public string City { get; set; }
public string CountryName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Person()
{
FirstName = NameFaker.Name();
LastName = NameFaker.LastName();
City = LocationFaker.City();
CountryName = LocationFaker.Country();
}
public static List<string> GetListOfAFirstNames(IEnumerable<Person> listOfPersons)
{
return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
}
public static List<string> GetListOfCities(IEnumerable<Person> listOfPersons)
{
return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
}
public static List<string> GetListOfCountries(IEnumerable<Person> listOfPersons)
{
return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
}
public static List<string> GetListOfLastNames(IEnumerable<Person> listOfPersons)
{
return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
}
}
}
}
I have a Some very not DRY code with the GetListOf... Methods
i feel like i should be able to do something like this
public static List<string> GetListOfProperty(
IEnumerable<Person> listOfPersons, string property)
{
return listOfPersons.Select(x =>x.property).Distinct().OrderBy(x=> x).ToList();
}
but that is not vaild code. I think the key Might Relate to Creating a Func
if That is the answer how do I do that?
Here is a second attempt using refelection But this is also a no go.
public static List<string> GetListOfProperty(IEnumerable<Person>
listOfPersons, string property)
{
Person person = new Person();
Type t = person.GetType();
PropertyInfo prop = t.GetProperty(property);
return listOfPersons.Select(prop).Distinct().OrderBy(x =>
x).ToList();
}
I think the refection might be a DeadEnd/red herring but i thought i would show my work anyway.
Note Sample Code is simplified in reality this is used to populate a datalist via AJAX to Create an autocomplete experience. That object has 20+ properties and I can complete by writing 20+ methods but I feel there should be a DRY way to complete this. Also making this one method also would clean up my controller action a bunch also.
Question:
Given the first section of code is there a way to abstract those similar methods into a single method buy passing some object into the select Statement???
Thank you for your time.
You would have to build the select
.Select(x =>x.property).
by hand. Fortunately, it isn't a tricky one since you expect it to always be the same type (string), so:
var x = Expression.Parameter(typeof(Person), "x");
var body = Expression.PropertyOrField(x, property);
var lambda = Expression.Lambda<Func<Person,string>>(body, x);
Then the Select above becomes:
.Select(lambda).
(for LINQ based on IQueryable<T>) or
.Select(lambda.Compile()).
(for LINQ based on IEnumerable<T>).
Note that anything you can do to cache the final form by property would be good.
From your examples, I think what you want is this:
public static List<string> GetListOfProperty(IEnumerable<Person>
listOfPersons, string property)
{
Type t = typeof(Person);
PropertyInfo prop = t.GetProperty(property);
return listOfPersons
.Select(person => (string)prop.GetValue(person))
.Distinct()
.OrderBy(x => x)
.ToList();
}
typeof is a built-in operator in C# that you can "pass" the name of a type to and it will return the corresponding instance of Type. It works at compile-time, not runtime, so it doesn't work like normal functions.
PropertyInfo has a GetValue method that takes an object parameter. The object is which instance of the type to get the property value from. If you are trying to target a static property, use null for that parameter.
GetValue returns an object, which you must cast to the actual type.
person => (string)prop.GetValue(person) is a lamba expression that has a signature like this:
string Foo(Person person) { ... }
If you want this to work with any type of property, make it generic instead of hardcoding string.
public static List<T> GetListOfProperty<T>(IEnumerable<Person>
listOfPersons, string property)
{
Type t = typeof(Person);
PropertyInfo prop = t.GetProperty(property);
return listOfPersons
.Select(person => (T)prop.GetValue(person))
.Distinct()
.OrderBy(x => x)
.ToList();
}
I would stay away from reflection and hard coded strings where possible...
How about defining an extension method that accepts a function selector of T, so that you can handle other types beside string properties
public static List<T> Query<T>(this IEnumerable<Person> instance, Func<Person, T> selector)
{
return instance
.Select(selector)
.Distinct()
.OrderBy(x => x)
.ToList();
}
and imagine that you have a person class that has an id property of type int besides those you already expose
public class Person
{
public int Id { get; set; }
public string City { get; set; }
public string CountryName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
all you need to do is fetch the results with type safe lambda selectors
var ids = listOfPersons.Query(p => p.Id);
var firstNames = listOfPersons.Query(p => p.FirstName);
var lastNames = listOfPersons.Query(p => p.LastName);
var cityNames = listOfPersons.Query(p => p.City);
var countryNames = listOfPersons.Query(p => p.CountryName);
Edit
As it seems you really need hardcoded strings as the property inputs, how about leaving out some dynamism and use a bit of determinism
public static List<string> Query(this IEnumerable<Person> instance, string property)
{
switch (property)
{
case "ids": return instance.Query(p => p.Id.ToString());
case "firstName": return instance.Query(p => p.FirstName);
case "lastName": return instance.Query(p => p.LastName);
case "countryName": return instance.Query(p => p.CountryName);
case "cityName": return instance.Query(p => p.City);
default: throw new Exception($"{property} is not supported");
}
}
and access the desired results as such
var cityNames = listOfPersons.Query("cityName");
You should be able to do it with Reflection. I use it something similar.
Just change your reflection try to this:
public static List<string> GetListOfValues(IEnumerable<Person> listOfPersons, string propertyName)
{
var ret = new List<string>();
PropertyInfo prop = typeof(Person).GetProperty(propertyName);
if (prop != null)
ret = listOfPersons.Select(p => prop.GetValue(p).ToString()).Distinct().OrderBy(x => x).ToList();
return ret;
}
I hope it helps.
It's based on C# 6
You can also use this. works for me.
public static class ObjectReflectionExtensions
{
public static object GetValueByName<T>(this T thisObject, string propertyName)
{
PropertyInfo prop = typeof(T).GetProperty(propertyName);
return prop.GetValue(thisObject);
}
}
And call like this.
public static List<string> GetListOfProperty(IEnumerable<Person> listOfPersons, string propertyName)
{
return listOfPersons.Select(x =>(string)x.GetValueByName(propertyName)).Distinct().OrderBy(x=> x).ToList();
}
If you want to select all the values:
object[] foos = objects.Select(o => o.GetType().GetProperty("PropertyName").GetValue(o)).ToArray();

C# Multiple generic types in single collection

There are a lot of answers for this using a single Type with interfaces and abstract classes which all work fine for one generic T value. What I am trying to achieve I have not seen and am wondering if anyone has an idea.
Scenario
public class Field<T>
{
public Expression<Func<T,object>> FieldName { get; set; }
}
public class FValue<T, F> : Field<T>
{
public F FieldValue { get; set; }
}
//Test
var fieldList = new List<Field<Person>>();
fieldList.Add(new FValue<Person, DateTime> { FieldName=x=>x.SomeDate, FieldValue=DateTime.Now });
fieldList.Add(new FValue<Person, string> { FieldName=x=>x.SomeData, FieldValue="test" });
Ideally i want to do the following:-
The list will contain the same type for T, the type for F will change to various types like date,string etc.
When iterating over the list i want both the FieldName and FieldValue
I can't start the list using new <List<FValue<Persion,string>>();
for the obvious reason that all F values will have to be string.
Also when obtaining the FieldName somehow the value should be casted to Expression<Func<T,F>>.
Any suggestions would be appreciated.
Whenever you want to use generics you need a specific reason to use it. See this answer about when to use Generics
What is cool about generics, why use them?
As you can see in the link, one of the main reason is to have type-safe properties. This also means that your class will be limited to the specific type. And taking this in consideration, the usages of your class will be limited.
Here is how I could use your class with limited usages that don't require (boxing/unboxing), but still requires casting
private static void UseFieldList<T>(List<Field<T>> fieldList)
{
foreach (var field in fieldList)
{
var propName = field.FieldNameText;
var textField = field as FValue<T, string>;
if (textField != null)
{
// Now can use string specific functions without (boxing/unboxing)
Console.WriteLine(propName + " " + textField.FieldValue );
continue;
}
var dateField = field as FValue<T, DateTime>;
if (dateField != null)
{
// Now can use date specific functions without (boxing/unboxing)
Console.WriteLine(propName + " " + dateField.FieldValue.ToShortDateString());
continue;
}
throw new NotSupportedException("The type of the field is not supported: " + field.GetType().Name);
}
}
To get the name out of a expression you can see the answer here
Retrieving Property name from lambda expression
And to use this I would change the way you are creating the objects to something similar to the usages of Tuple:
// Newer code storing name
fieldList.Add(FValue<Person>.Create(x => x.DateOfBirth, DateTime.Now ));
fieldList.Add(FValue<Person>.Create(x => x.Name, "test"));
// Old code storing expression instead of name
fieldList.Add(new FValue<Person, DateTime> { FieldName = x => x.DateOfBirth, FieldValue = DateTime.Now });
fieldList.Add(new FValue<Person, string> { FieldName = x => x.Name, FieldValue = "test" });
// Not supported Type Int
fieldList.Add(new FValue<Person, int> {FieldName = x => x.Name, FieldValue = 12});
And here is the factory class
public class FValue<T>
{
public static Field<T> Create<F>(Expression<Func<T, F>> fieldNameExpression, F value)
{
return new FValue<T, F>
{
FieldNameText = GetPropertyInfo(fieldNameExpression).Name,
FieldValue = value
};
}
}
Results of the console:
DateOfBirth 1/19/2017
Name test
x => Convert(x.DateOfBirth) 1/19/2017
x => x.Name test
The type of the field is not supported: FValue`2
I'm not sure I see a way around this one without using Reflection. Using these classes:
public class Field<T>
{
public Expression<Func<T, object>> FieldName { get; set; }
}
public class FValue<T, F> : Field<T>
{
public F FieldValue { get; set; }
}
You can iterate over them like so:
var list = new List<Field<string>>();
list.Add(new FValue<string, int>() { FieldName = null, FieldValue = 5 });
foreach (var x in list)
{
Type[] types = x.GetType().GetGenericArguments();
// Dirty check to confirm this is an FValue not a Field
if (types.Length == 2)
{
var fieldName = x.FieldName;
object fieldValue = x.GetType().GetProperty("FieldValue").GetValue(x);
// fieldValue will be "5"
}
}

Copying All Class Fields and Properties To Another Class

I have a class which normally contains Fields, Properties. What i want to achieve is instead of this:
class Example
{
public string Field = "EN";
public string Name { get; set; }
public int? Age { get; set; }
public List<string> A_State_of_String { get; set; }
}
public static void Test()
{
var c1 = new Example
{
Name = "Philip",
Age = null,
A_State_of_String = new List<string>
{
"Some Strings"
}
};
var c2 = new Example();
//Instead of doing that
c2.Name = string.IsNullOrEmpty(c1.Name) ? "" : c1.Name;
c2.Age = c1.Age ?? 0;
c2.A_State_of_String = c1.A_State_of_String ?? new List<string>();
//Just do that
c1.CopyEmAll(c2);
}
What i came up with but doesn't work as expected.
public static void CopyEmAll(this object src, object dest)
{
if (src == null) {
throw new ArgumentNullException("src");
}
foreach (PropertyDescriptor item in TypeDescriptor.GetProperties(src)) {
var val = item.GetValue(src);
if (val == null) {
continue;
}
item.SetValue(dest, val);
}
}
Problems:
Although i checked for null, it seems to bypass it.
Doesn't seem to copy Fields.
Notes:
I don't want to use AutoMapper for some technical issues.
I want the method to copy values and not creating new object. [just mimic the behavior i stated in the example]
I want the function to be recursive [if the class contains another classes it copies its values too going to the most inner one]
Don't want to copy null or empty values unless i allow it to.
Copies all Fields, Properties, or even Events.
Based on Leo's answer, but using Generics and copying also the fields:
public void CopyAll<T>(T source, T target)
{
var type = typeof(T);
foreach (var sourceProperty in type.GetProperties())
{
var targetProperty = type.GetProperty(sourceProperty.Name);
targetProperty.SetValue(target, sourceProperty.GetValue(source, null), null);
}
foreach (var sourceField in type.GetFields())
{
var targetField = type.GetField(sourceField.Name);
targetField.SetValue(target, sourceField.GetValue(source));
}
}
And then just:
CopyAll(f1, f2);
You can use serialization to serialize object A and deserialize as object B - if they have very same structure, you can look here for object deep copy.
Deep cloning objects
I know you don't want to use Automapper, but if the types have only SIMILAR structure, you should maybe use Automapper which is based on reflection. You can download a nuget and find some information here:
https://www.nuget.org/packages/AutoMapper/
your code then will look like
public TOutput CopyAll<TInput, TOutput>(TInput input)
{
var config = new MapperConfiguration(cfg => cfg.CreateMap<TInput, TOutput>());
IMapper mapper = config.CreateMapper();
return mapper.Map<TOutput>(vstup);
}

Adding new T to empty List<T> using reflection

I'm attempting to set add a new instance of an Officer class to a potentially empty list using reflection.
These are my classes
public class Report(){
public virtual ICollection<Officer> Officer { get; set; }
}
public class Officer(){
public string Name{ get; set; }
}
Simplified code snippet:
Report report = new Report()
PropertyInfo propertyInfo = report.GetType().GetProperty("Officer");
object entity = propertyInfo.GetValue(report, null);
if (entity == null)
{
//Gets the inner type of the list - the Officer class
Type type = propertyInfo.PropertyType.GetGenericArguments()[0];
var listType = typeof(List<>);
var constructedListType = listType.MakeGenericType(type);
entity = Activator.CreateInstance(constructedListType);
}
//The entity is now List<Officer> and is either just created or contains a list of
//Officers
//I want to check how many officers are in the list and if there are none, insert one
//Pseudo code:
if (entity.count = 0)
{
entity.add(new instance of type)
}
Much appreciated!
Use:
object o = Activator.CreateInstance(type); // "type" is the same variable you got a few lines above
((IList)entity).add(o);
You have two options:
1) Using dynamic:
dynamic list = entity;
if (list.Count = 0)
{
list.Add(new instance of type)
}
2) Using Reflection:
var countProp = entity.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).First(p => p.Name == "Count");
var count = (int)countProp.GetValue(entity,null);
if(count == 0)
{
var method = entity.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public).First(m => m.Name == "Add");
method.Invoke(entity,new instance of type);
}
This isn't quite what you asked for but may accomplish the same task.
public static ICollection<T> EnsureListExistsAndHasAtLeastOneItem(ICollection<T> source)
where T : Officer, new()
{
var list = source ?? new List<T>();
if( list.Count == 0 ) list.Add(new T());
return list;
}
If Officer doesn't have a default constructor then you could add a factory callback
public static ICollection<T> EnsureListExistsAndHasAtLeastOneItem
(ICollection<T> source, Func<T> builder)
where T : Officer
{
var list = source ?? new List<T>();
if( list.Count == 0 ) list.Add(builder());
return list;
}
Just type your entity appropriately as a List<Officer> (or an appropriately more abstract type (such as IList)) and use as normal:
entity = Activator.CreateInstance(constructedListType) as IList;
But no need to check whether to insert or not, just insert:
entity.Insert(0, officer);
I'm assuming (based on the fact that you already know how to create instances using reflection) you're not having trouble creating the instance of type Officer.
Edit after re-reading over your question: This doesn't directly answer your question but is rather a suggestion of a different implementation.
You can easily get by without using reflection:
public class TestContainer<T>
{
private readonly List<T> _list;
public TestContainer()
{
_list = new List<T>();
}
public void Add()
{
_list.Add(default(T));
}
}
Then calling e.g.:
var t = new TestContainer<YourClass>();
t.Add();
t.Add();
t.Add();
you will have a list of 3 instances of YourClass by their default value

Categories