【问题标题】:How can i make this linq query cleaner and more reusable?我怎样才能使这个 linq 查询更干净、更可重用?
【发布时间】:2021-11-12 13:37:01
【问题描述】:

我有一个 linq 查询,它返回几个环大小的计数。然而,即使它在某种程度上是可重复使用的,我想看看我是否可以改进它。

    private async Task<RingSizeLettersDto> CountOfLettersByRingFingerAsync(Func<RingSize, string> selector)
    {
        var ringSizes = await _ringSizeRepository.Get();

        RingSizeLettersDto ringSizeLetters = new RingSizeLettersDto();

        ringSizeLetters.G = ringSizes.Where(x => selector(x) == "G").Count();
        ringSizeLetters.H = ringSizes.Where(x => selector(x) == "H").Count();
        ringSizeLetters.I = ringSizes.Where(x => selector(x) == "I").Count();
        ringSizeLetters.J = ringSizes.Where(x => selector(x) == "J").Count();
        ringSizeLetters.K = ringSizes.Where(x => selector(x) == "K").Count();
        ringSizeLetters.L = ringSizes.Where(x => selector(x) == "L").Count();
        ringSizeLetters.M = ringSizes.Where(x => selector(x) == "M").Count();
        ringSizeLetters.N = ringSizes.Where(x => selector(x) == "N").Count();
        ringSizeLetters.O = ringSizes.Where(x => selector(x) == "O").Count();
        ringSizeLetters.P = ringSizes.Where(x => selector(x) == "P").Count();
        ringSizeLetters.Q = ringSizes.Where(x => selector(x) == "Q").Count();
        ringSizeLetters.R = ringSizes.Where(x => selector(x) == "R").Count();
        ringSizeLetters.S = ringSizes.Where(x => selector(x) == "S").Count();
        ringSizeLetters.T = ringSizes.Where(x => selector(x) == "T").Count();
        ringSizeLetters.U = ringSizes.Where(x => selector(x) == "U").Count();
        ringSizeLetters.V = ringSizes.Where(x => selector(x) == "V").Count();
        ringSizeLetters.W = ringSizes.Where(x => selector(x) == "W").Count();
        ringSizeLetters.X = ringSizes.Where(x => selector(x) == "X").Count();
        ringSizeLetters.Y = ringSizes.Where(x => selector(x) == "Y").Count();
        ringSizeLetters.Z = ringSizes.Where(x => selector(x) == "Z").Count();
        ringSizeLetters.Z1 = ringSizes.Where(x => selector(x) == "Z1").Count();
        ringSizeLetters.Z2 = ringSizes.Where(x => selector(x) == "Z2").Count();
        ringSizeLetters.Z3 = ringSizes.Where(x => selector(x) == "Z3").Count();
        ringSizeLetters.Z4 = ringSizes.Where(x => selector(x) == "Z4").Count();
        ringSizeLetters.Z5 = ringSizes.Where(x => selector(x) == "Z5").Count();
        ringSizeLetters.Z6 = ringSizes.Where(x => selector(x) == "Z6").Count();
        ringSizeLetters.NA = ringSizes.Where(x => selector(x) == "N/A").Count();

        return ringSizeLetters;

    }

【问题讨论】:

  • 也许在这种情况下,您最好循环一次ringsizes 并手动有条件地递增计数器?也就是说,如果性能是一个问题
  • 在不了解您的具体用例的情况下,似乎可以更简洁地建模。它可以是 Dictionary,其中 string 是字母,int 是计数。或者你可以有一个更通用的模型,而不是每个字母都有单独的属性,你只需要一个名为“Letter”(和 int count)的字符串。在这两种情况下,您都可以使用一个查询。
  • ringSizeLetters.Z6 = ringSizes.Count(x =&gt; selector(x) == "Z6") 也可以

标签: c# .net linq coding-style


【解决方案1】:

我想到了两个改进:

  • 您可以使用Count(x =&gt; selector(x) == "G"),这将减少每行1个方法调用。

  • 您可以创建一个Dictionary&lt;string, int&gt;(可能是char,因为键可以更准确)表示按字符计数,然后在应用程序的其他地方您可以使用要搜索的字符作为键来访问它:

var ringSizes = new[] { 1, 2, 2 };
var ringSizeLetters = ringSizes.GroupBy(x => Selector(x))
     .ToDictionary(x => x.Key, v => v.Count());

var ocurrences = ringSizeLetters["A"];


【讨论】:

    【解决方案2】:

    根据您的特定用例是否允许这样做,更简洁的解决方案是使用字典。从您的代码来看,最好的类型是IDictionary&lt;string, int&gt;。其中,字符串键为标识符(如'Z1'),整数值为.Count()的结果。

    我在下面提供了一个可能的代码示例;

    public static IDictionary<string, int> GetRingSizeLettersAsDict(
        IEnumerable<string> sizeLetters,
        IEnumerable<object> ringSizes)
    {
        IDictionary<string, int> ringSizeLetters = new();
    
        sizeLetters.Select(
            sl => ringSizeLetters.Add(
                sl,
                ringSizes.Where(rs => selector(rs).Equals(sl)).Count())
        );
    
        return ringSizeLetters;
    }
    

    这只是众多解决方案之一,但它应该与您的代码示例相同,前提是它适合您的用例。

    【讨论】:

      【解决方案3】:

      这是一种伪代码,但类似这样的东西应该可以解决问题..

      这是通过 System.Reflection 完成的

                      PropertyInfo[] properties = ringSizeLetters.GetProperties();
                      foreach (PropertyInfo property in properties)
                      {
                         var propertyName = property.Name;
                         var ringsizeValue = ringSizes.SingleOrDefault(x=> x.PROPERTYNAME == propertyName)
          
                         // if it finds ringsize Value 
                        if(ringsizeValue != null)
                        {
                           property.SetValue(ringSizeLetters, ringsizeValue.Count());
                        }
          
                      }
      

      【讨论】:

        【解决方案4】:

        如果您真的需要,您可以保留所有属性:

        class RingSizeDto{
        
          public int G { get; set;}
          public int H { get; set;}
        

        ... 并通过例如支持它们来避免反射一本字典:

        class RingSizeDto{
        
          private Dictionary<string, int> _ringSizes = new();
        
          public int G { get => _ringSizes[nameof(G)]; set _ringSizes[nameof(G)] = value; }
          public int H { get => _ringSizes[nameof(H)]; set _ringSizes[nameof(H)] = value; }
          ...
        

        这意味着可以通过提供字典一次性设置大小:

          public RingSizeDto(Dictionary <string, int> sizes){
            _ringSizes = sizes;
          }
        

        字典可以通过查询生成,例如

        var sizes = someSizeList.GroupBy(s => s.SizeLetter).ToDictionary(g => g.Key, g => g.Count());
        
        new RingSizeDto(sizes);
        

        注意;如果您的列表没有所有可能的大小,您需要使用 TryGetValue 来避免字典访问崩溃

        public int G { get => _ringSizes.TryGetValue(nameof(G), out var x) ? x : 0; set _ringSizes[nameof(G)] = value; }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-11-01
          • 2013-08-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多