user194076
user194076

Reputation: 9017

Select property by string name using LINQ

I have a pretty simple piece of code:

var list = new List<MyType>();
list = MyItems.Where(x => x.Name.ToLower().Contains(e.Text.ToLower())).ToList();
for (int i = itemOffset; i < endOffset; i++)
{
    combo.Items.Add(new ComboItem(list[i].Name, list[i].Id.ToString()));
}

What I want to do is to make this a generic function that accepts list of any type and accepts two strings as properties. (In this case two properties that we are looking at are Name and Id.

private void MakeCombo<T>(Combo combo, IEnumerable<T> lst, string Field1, string Field2)
{
    var list = list = lst.Where(x => x.Field1.ToLower().Contains(searchText.ToLower())).ToList(); //How make this to work???

    for (int i = itemOffset; i < endOffset; i++)
    {
        combo.Items.Add(new ComboItem(list[i].Field1, list[i].Field2.ToString())); //How make this to work???
    }
}

Now, I do not understand how can I access properties of generic list by string names (Field1 and Field2).

Upvotes: 3

Views: 9117

Answers (4)

Chris
Chris

Reputation: 1725

You will have to use reflection:

typeof(x).GetField("Field1", BindingFlags.Public).GetValue()

You could do that for both objects and compare the values.

Upvotes: 2

Servy
Servy

Reputation: 203821

One way of solving this problem, if you don't have the ability of creating an interface and ensuring all of the items in the sequence implement that interface (as shown in this other answer), is to use delegates. By passing in two functions, each of which take a T as input, and return a string or object as the output, you can mimic that functionality:

private void MakeCombo<T>(Combo combo, IEnumerable<T> sequence
    , Func<T, string> field1Selector, Func<T, object> field2Selector)
{
    var foundItems = sequence.Where(item => field1Selector(item).ToLower()
        .Contains(searchText.ToLower()));

    foreach(var item in foundItems)
    {
        combo.Items.Add(new ComboItem(field1Selector(item), field2Selector(item).ToString()));
    }
}

From the calling side it might look something like this (to use your first code snippet as an example:

MakeCombo(combo, list, item => item.Name, item => item.Id);

Upvotes: 5

Tommaso Belluzzo
Tommaso Belluzzo

Reputation: 23675

private void MakeCombo<T>(Combo combo, IEnumerable<T> list)
{
    Type type = typeof(T);
    List<T> comboList = list.Where(x => ((String)type.GetField("Field1", (BindingFlags.Public | BindingFlags.Instance)).GetValue(x)).ToLower().Contains(m_SearchText.ToLower()));

    for (Int32 i = m_ItemOffset; i < m_OffsetEnd; ++i)
        combo.Items.Add(new ComboItem((String)type.GetField("Field1", (BindingFlags.Public | BindingFlags.Instance)).GetValue(list[i]), (String)type.GetField("Field2", (BindingFlags.Public | BindingFlags.Instance)).GetValue(list[i])));
}

Upvotes: 1

horgh
horgh

Reputation: 18534

If you could define an interface, describing all the fields available, and make each type, that is suitable for this method implement that interface, it would look like this (and would be IMO better, than accessing fields by names):

public interface IFields
{
    string Prop1 { get; set; }
    string Prop2 { get; set; }
}

public static void DoSmth<T>(List<T> items) where T : IFields
{
    foreach (T t in items)
    {
        Console.WriteLine(t.Prop1);
        Console.WriteLine(t.Prop2);
    }
}

Upvotes: 1

Related Questions