【问题标题】:LINQ Select() or ForEach loopLINQ Select() 或 ForEach 循环
【发布时间】:2012-12-19 04:05:35
【问题描述】:

我是仿制药新手,但我知道我需要使用它们来帮助保持干燥 :)

我正在尝试创建一个类,它将许多不同类型的列表转换为一个由下拉列表控件使用但无法使其工作的通用类。我在下面的方法中展示了 2 个选项来尝试这个,试图让每个选项都工作(一个 foreach 循环和一个 linq),但无济于事。

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
           };
}

【问题讨论】:

  • 你遇到了什么错误
  • 有什么理由不能使用类的接口来强制执行“必须具有 .Id 值”语义?然后在 T 上的 where 子句将其约束到该接口?

标签: c# linq generics foreach


【解决方案1】:

您可以使用接口来约束 T 以包含 Id 属性,并且您可以使用 Func&lt;T,string&gt; 访问 T 类型对象的任何给定属性,同时保持类型安全并避免反射:

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)
    });
}

【讨论】:

  • @IanMercer 感谢伊恩。我有这个工作,除了实际调用这个方法。我有 GetDDLOptionsViewModel(vendors.ToList(), "VendorName"); Func 的 T 参数是什么?
  • 好吧,终于弄明白了,好吧。我最终通过 GetDDLOptionsViewModel(vendors.ToList(), x => x.Id, x => x.VendorName) 调用。但是我从使用接口 IId 改变了,因为对于一个我无法让它工作......正在收到关于没有隐式转换为 IId 的错误,并且因为列表上可能并不总是有一个 Id 属性传递给这个方法。所以我将方法参数更改为 (IEnumerable list, Func propId, Func propDisplayName)
【解决方案2】:

甚至更短(LINQ 也是如此),一行和一个扩展方法:

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

另外我会用大写字母命名一个类,避免使用复数形式。

不要忘记通过调用ToArray()ToList() 来具体化任何LINQ 查询结果。


将属性名称转换为属性定位 lambda 表达式:

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;
}

用法:

// assuming class X { public string PropertyName { get; set; }
var lambda = "PropertyName".ToLambda<X>()
var x = new X { PropertyName = "Hi!" };
string value = lambda(x); // "Hi!"

【讨论】:

  • 感谢您的回复和建议。我试过这个,但问题是源属性无法识别(即 l.Id 和 l.strPropName)。另外, strPropName 只是一个字符串,所以不确定它将如何将其转换为属性?你知道如何处理那部分吗?
  • @ChadRichardson:请参阅this CodeProject article 如何将表示属性的字符串转换为指向该属性的 lambda 表达式。=,即PropertyName -> x =&gt; x.PropertyName
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-12
  • 2017-02-19
  • 1970-01-01
  • 2013-07-08
  • 1970-01-01
  • 2017-04-21
相关资源
最近更新 更多