LINQ Select() or ForEach loop

11,231

Solution 1

You could use an interface to constrain T to include an Id property and you could use a Func<T,string> to access any given property off an object of type T all while preserving type safety and avoiding reflection:

public interface IId
{
  string Id {get;}
}


private static IEnumerable<ddlOptions> GetDDLOptionsViewModel<T>
    (IEnumerable<T> list, Func<T,string> propAccess)
   where T:IId
{
   return list.Select(l => new DdlOption
    {
        Id = l.Id,
        DisplayName = propAccess(l)
    });
}

Solution 2

Even shorter (LINQ as well), one line and an extension method:

public static IEnumerable<DdlOption> ToDdlOption<T>(this IEnumerable<T> genericList, string strPropName)
{
    return genericList.Select(l => new DdlOption
        {
            Id = l.Id,
            DisplayName = l.strPropName
        });
}

Also I would name a class with capital letter and avoid plural form.

And don't forget to materialize any LINQ query result by calling ToArray() or ToList().


To convert property name to property targeting lambda expression:

public static Func<T, object> ToLambda<T>(this string propertyName)
{
    ParameterExpression param = Expression.Parameter(typeof(T), "x"); // x
    Expression property = Expression.Property(param, propertyName);   // x.PropertyName
    Func<T, object> lambda = Expression.Lambda<Func<T, object>>(      // x => x.PropertyName
            Expression.Convert(property, typeof(object)),
            param)
        .Compile();
    return lambda;
}

Usage:

// assuming class X { public string PropertyName { get; set; }
var lambda = "PropertyName".ToLambda<X>()
var x = new X { PropertyName = "Hi!" };
string value = lambda(x); // "Hi!"
Share:
11,231
crichavin
Author by

crichavin

Updated on June 04, 2022

Comments

  • crichavin
    crichavin almost 2 years

    I am new to generics but know that I need to use them to help keep things DRY :)

    I am trying to create a class that converts many different types of Lists to a common class that is used by a dropdownlist control but can't get it to work. I am showing 2 options for trying this in the below method, trying to get each to work (a foreach loop and a linq), but to no avail.

    private static IEnumerable<ddlOptions> GetDDLOptionsViewModel<T>(List<T> genericList, string strPropName)
    {
        // option A: ForEach loop
        Type type = genericList.GetType();
        var IdProperty = type.GetProperty("Id");
        var DisplayNameProp = type.GetProperty(strPropName);
    
        List<ddlOptions> options = new List<ddlOptions>();
        foreach (var l in genericList)
        {
            options.Add(new ddlOptions
                { 
                    Id = IdProperty.GetValue(l, null), 
                    DisplayName = DisplayNameProp.GetValue(l, null)
                });
        }
        return options;
    
        // option B - LINQ
        return from l in genericList
               select new ddlOptions
               {
                   Id = l.Id,
                   DisplayName = l.strPropName
               };
    }
    
  • crichavin
    crichavin over 11 years
    thank you for the response and suggestions. I tried this but the issue is that the source properties are not recognized (i.e. l.Id and l.strPropName). Also, strPropName is just a string so not sure how it will convert that to a property? Do you know how to handle that part?
  • abatishchev
    abatishchev over 11 years
    @ChadRichardson: See this CodeProject article how to convert a string representing the property to the lambda expression pointing to this property.=, i.e. PropertyName -> x => x.PropertyName.
  • crichavin
    crichavin over 11 years
    @IanMercer thanks Ian. I have this working with the exception of actually calling this method. I have GetDDLOptionsViewModel(vendors.ToList(), "VendorName"); What is the T parameter of Func expecting?
  • crichavin
    crichavin over 11 years
    Ok, figured it out finally, well mostly. I ended up calling via GetDDLOptionsViewModel(vendors.ToList(), x => x.Id , x => x.VendorName). But I changed from using the interface IId, because for one I couldn't get it to work...was getting error about no implicit conversion to IId, and because there may not always be an Id property on the lists being passed to this method. So I changed the method params to (IEnumerable<T> list, Func<T, int> propId, Func<T, string> propDisplayName)