【问题标题】:It is possible to create a generic search method where key is unknown可以创建密钥未知的通用搜索方法
【发布时间】:2016-08-24 10:13:11
【问题描述】:

是否可以在密钥未知的情况下创建通用搜索方法?例如,列表的键将传递给参数,它执行类似搜索并返回过滤后的列表。

代码应该是这样的:

public List<T> LikeSearch<T>(List<T> AllData,T key, string searchString)
{
  List<T> _list = new List<T>();
  //Perform the search on AllData based on searchString passed on the key   
  //given
 return _list;
}

用途如下:

示例 1

List<Users> _users = LikeSearch<Users>(AllUsers,'Name','sam');

其中AllUsers 是100 个users 的列表。

示例 2

List<Customers> _cust = LikeSearch<Customers>(AllCustomers,'City','London');

其中AllCustomers 是100 个Customers 的列表。

请指教

【问题讨论】:

  • 为什么keyT?好像是属性名,不应该一直是string吗?这可以使用反射来解决。会很慢……
  • 是的,对的很抱歉....键将永远是字符串...
  • 看看Dynamic LINQ
  • 为什么不简单地使用 linq?例如。 _users.Where(user =&gt; user.Name == "sam")_cust.Where(cust =&gt; cust.City == London) ?它看起来并不比您的 LikeSearch 用法示例长多少。
  • @Sinatr 我认为这是因为 OP 只知道成员的 name,他不能直接调用它。你需要反思。 key 实际上是一个字符串,根据上面的 cmets。

标签: c# linq generics


【解决方案1】:

假设 key 始终引用由 T 的任何类型实现的公共属性,您可以执行以下操作:

public static List<T> LikeSearch<T>(this List<T> data, string key, string searchString)
{
    var property = typeof(T).GetProperty(key, BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Instance);

    if (property == null)
        throw new ArgumentException($"'{typeof(T).Name}' does not implement a public get property named '{key}'.");

    //Equals
    return data.Where(d => property.GetValue(d).Equals(searchString)).ToList();

    //Contains:
    return data.Where(d => ((string)property.GetValue(d)).Contains(searchString)).ToList();
}

【讨论】:

  • @AbhinawKaushik 欢迎您,我们很乐意为您提供帮助。我已经稍微修正了代码,请确保您使用的是最新版本。
  • 再次感谢@InBetween 我已经更改为 contains 供我使用。非常感谢。
  • 非常感谢!你解决了我的通用 Linq 搜索! List&lt;PropertyInfo&gt; searchableProperties; if (!string.IsNullOrEmpty(pagedRequest.SearchQuery)) { searchableProperties = objectType.GetProperties() .Where(p =&gt; p.GetCustomAttributes().OfType&lt;Searchable&gt;.Any()) .ToList(); query = query.Where(q =&gt; searchableProperties.Any(p =&gt; p.GetValue(q).ToString() == pagedRequest.SearchQuery)); }
【解决方案2】:

我认为this link 会帮助您...问题有所不同,但您可以在那里找到答案。作为参考,我再次在此处发布答案...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Linq.Expressions;
using System.Reflection;
namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {

            List<Demo> all= new List<Demo>();
            all.Add(new Demo{Name="a"});
            all.Add(new Demo{Name="ab"});
            all.Add(new Demo{Name="abc"});
            all.Add(new Demo{Name="cba"});
            all.Add(new Demo{Name="bac"});
            all.Add(new Demo{Name="ddd"});

            var t= Filter(all,"Name","a");

            Console.WriteLine(t.Count);
        }

        public static List<T> Filter<T>(List<T> Filterable, string PropertyName, object ParameterValue)
        {
          ConstantExpression c = Expression.Constant(ParameterValue);
          ParameterExpression p = Expression.Parameter(typeof(T), "xx");
          MemberExpression m = Expression.PropertyOrField(p, PropertyName);

          MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });  

          var containsMethodExp = Expression.Call(m, method, c);
          var Lambda= Expression.Lambda<Func<T, bool>>(containsMethodExp, p);           

          //var Lambda = Expression.Lambda<Func<T, Boolean>>(Expression.Equal(c, m), new[] { p });

          Func<T, Boolean> func = Lambda.Compile();
          return Filterable.Where(func).ToList();
        }
    }

    public class Demo
    {
        public string Name{get;set;}
    }
}

【讨论】:

  • Moumit...感谢您的帮助,这适用于 Equals 场景,我正在寻找包含搜索。如果可能的话,我认为我需要使用 Expression.Equals 来获取 Expression.Contains。无论如何,非常感谢。
  • @AbhinawKaushik ...您将采用哪种解决方案并不重要...使用ExpressionTree您也可以实现这一目标..
【解决方案3】:

用linq方法Where

list.Where(x => x.YourKey.Contains(searchString))

示例 1

List<Users> _users = AllUsers.Where(x => x.Name.Contains("sam"));

示例 2

List<Customers> _cust = AllCustomers.Where(x => x.City.Contains("London"));

否则你可以写一个这样的方法:

public List<T> LikeSearch<T>(List<T> list, Func<T, string> getKey, string searchString)
{
    return list.Where(x => getKey(x).Contains(searchString)).ToList();
}

你可以这样使用它:

示例 1

List<Users> _users = LikeSearch(AllUsers, x => x.Name, "sam");

示例 2

List<Customers> _cust = LikeSearch(AllCustomers, x => x.City, "London");

编辑:这里是关于这里提出的解决方案的小基准

我只对每个人的Contains 版本进行了基准测试。

有了这个我们可以看到(取决于你的电脑和星星...):

InBetween OneProperty: 00:00:00.0026050

Moumit OneProperty:00:00:00.0013360

我的 OneProperty:00:00:00.0010390

两个不同的类在这里测试属性的数量是否有变化

在 LotProperties 之间:00:00:00.0026378

Moumit LotProperties:00:00:00.0012155

我的 LotProperties:00:00:00.0010021

我真的很惊讶 Moumit 的解决方案如此之快,我预计它在运行时编译会更慢。但是,我们可以看到GetPropertyGetValue 确实很慢。

基准代码:

    static void Main(string[] args)
    {
        int size = 10000;
        Dictionary<string, List<long>> time = new Dictionary<string, List<long>>()
        {
            {"InBetween OneProperty", new List<long>() },
            {"Moumit OneProperty", new List<long>() },
            {"Mine OneProperty", new List<long>() },
            {"InBetween LotProperties", new List<long>() },
            {"Moumit LotProperties", new List<long>() },
            {"Mine LotProperties", new List<long>() },
        };
        List<OneProperty> oneProperties = new List<OneProperty>();
        List<LotProperties> lotProperties = new List<LotProperties>();
        for (int i = 0; i < size; ++i)
        {
            oneProperties.Add(new OneProperty() { Key = i.ToString() });
            lotProperties.Add(new LotProperties() { Key = i.ToString() });
        }
        Stopwatch sw = new Stopwatch();
        for (int i = 0; i < 1000; ++i)
        {
            sw.Start();
            InBetween.LikeSearch(oneProperties, "Key", "999");
            sw.Stop();
            time["InBetween OneProperty"].Add(sw.Elapsed.Ticks);
            sw.Reset();
            sw.Start();
            Moumit.Filter(oneProperties, "Key", "999");
            sw.Stop();
            time["Moumit OneProperty"].Add(sw.Elapsed.Ticks);
            sw.Reset();
            sw.Start();
            Mine.LikeSearch(oneProperties, x => x.Key, "999");
            sw.Stop();
            time["Mine OneProperty"].Add(sw.Elapsed.Ticks);
            sw.Reset();

            sw.Start();
            InBetween.LikeSearch(lotProperties, "Key", "999");
            sw.Stop();
            time["InBetween LotProperties"].Add(sw.Elapsed.Ticks);
            sw.Reset();
            sw.Start();
            Moumit.Filter(lotProperties, "Key", "999");
            sw.Stop();
            time["Moumit LotProperties"].Add(sw.Elapsed.Ticks);
            sw.Reset();
            sw.Start();
            Mine.LikeSearch(lotProperties, x => x.Key, "999");
            sw.Stop();
            time["Mine LotProperties"].Add(sw.Elapsed.Ticks);
            sw.Reset();
        }
        foreach (string key in time.Keys)
            Console.WriteLine($"{key}: {new TimeSpan((long)time[key].Average())}");
        Console.ReadKey();
    }

    class OneProperty
    {
        public string Key { get; set; }
    }
    class LotProperties
    {
        public string A { get; set; }
        public string B { get; set; }
        public string C { get; set; }
        public string D { get; set; }
        public string E { get; set; }
        public string F { get; set; }
        public string G { get; set; }
        public string H { get; set; }
        public string I { get; set; }
        public string J { get; set; }
        public string K { get; set; }
        public string L { get; set; }
        public string M { get; set; }
        public string N { get; set; }
        public string O { get; set; }
        public string P { get; set; }
        public string Q { get; set; }
        public string R { get; set; }
        public string S { get; set; }
        public string T { get; set; }
        public string U { get; set; }
        public string V { get; set; }
        public string W { get; set; }
        public string X { get; set; }
        public string Y { get; set; }
        public string Z { get; set; }
        public string Key { get; set; }
    }

【讨论】:

  • OP只知道City的名字,不能直接调用属性。
  • 你在哪里读到的?他说属性是未知的,可能因为名字可以改变,但他知道名字,不然怎么写key string?如果他不知道,您的解决方案仍然与我的问题相同。
  • 来自问题中的用法示例和评论。我不会跟随你的解决方案有同样的问题。我使用反射来查找具有给定名称的属性。
  • 伙计们,未知键意味着我的 List 可以是任何类,而 Key 将是该类的字符串属性之一;因此,在为搜索编写通用通用方法时,我不知道将传递哪个类以及键名是什么。希望我在这里说清楚。
  • @AbhinawKaushik 您可能想查看我的编辑,您可能想更改保留的解决方案。
猜你喜欢
  • 1970-01-01
  • 2011-05-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-10
相关资源
最近更新 更多