【问题标题】:IEnumerable.Select() for a single itemIEnumerable.Select() 用于单个项目
【发布时间】:2023-04-09 04:47:01
【问题描述】:

我最近创建了这个扩展方法:

public static TR Calculate<TS, TR>(this TS item, Func<TS, TR> selector)
    => selector(item);

然后,我可以重构这段丑陋的代码,来自:

int value = panel.Controls.Cast<Control>().Last().Location.Y
       + panel.Controls.Cast<Control>().Last().Size.Height;

到:

int value = panel.Controls.Cast<Control>().Last().Calculate(
    o => o.Location.Y + o.Size.Height);

我想知道是否有办法使用内置的 .NET 函数来代替?

【问题讨论】:

  • 什么是先天功能?
  • 您尝试过 System.Linq 吗?
  • @BradleyUffner 是的,但这需要IEnumerable&lt;T&gt; 而不是T。上面说了,输入输出是标量,所以IEnumerable是无关紧要的。
  • @MatthewWatson MSDN Says 它返回T 的单个实例,而不是IEnumerable&lt;T&gt;
  • SingleSelect 是一个奇怪的名字,因为它接受了一件事情并传递了一件事情。 LINQ 通常不这样做。我认为CalculateExecute 会是一个更好的名字。不过我还是不喜欢这种模式。

标签: c#


【解决方案1】:

“先天”,我猜你的意思是你自己不想使用任何新方法?

好吧,你可以这样做:

value = ((Func<InputType, OutputType>)(o => o.First + o.Second + o.Property.Value))(collection[calc(param)]);

基本上,将 lambda 转换为委托类型,然后调用委托。

或者也许我把这件事复杂化了。你真正想要的可能只是不要多次写collection[calc(param)]。如果是这种情况,您可以:

var o = collection[calc(param)];
value = o.First + o.Second + o.Property.Value;

我不明白你为什么不能那样做。

我真的建议你为这种转换编写一个单独的 a 方法。它将更具可读性。

// name this properly so that it describes what you actually want.
public OutputType TransformCollectionItem(TInput o) {
    return o.First + o.Second + o.Property.Value;
} 

【讨论】:

  • 就是这样,但我同意这个解决方案不容易阅读/理解。我是否可以得出结论,我必须保持目前的方法?我实际上可以这样做,但我不喜欢为“单行使用”创建变量。
  • @MaximeR。甚至不要为此创建SingleSelect。只需创建一个专门用于转换数据的方法。
【解决方案2】:

我会说你不应该这样做 - 我不认为它真的让代码更具可读性。

给定原始代码:

int value = panel.Controls.Cast<Control>().Last().Location.Y
          + panel.Controls.Cast<Control>().Last().Size.Height;

您的扩展方法允许您这样做:

int value = panel.Controls.Cast<Control>().Last().Calculate(
    o => o.Location.Y + o.Size.Height);

但这样做似乎没有什么优势:

var c = panel.Controls.Cast<Control>().Last();
int value = c.Location.Y + c.Size.Height;

后者实际上更短,并且不需要读者了解扩展方法。

此外,您的扩展方法将出现在所有东西的工具提示中...

请注意,它确实允许一些有趣的代码,例如

double negated = 1.0.Calculate(x => -x);

【讨论】:

  • 我添加了where TS : class 以避免您的最后一点。即使它会禁用 Size、Color 等结构的使用,现在也足够了。
【解决方案3】:

您在此处描述的操作通常称为“应用”,因为它只是将函数应用于参数作为扩展方法。您要求一种“与生俱来”的方法;我不太清楚你的意思。 函数应用程序已经内置在 C# 语法中。要将函数 F 应用于参数 a 以在 C# 中获得结果 r,您可以这样做:

r = F(a)

因此,在您的情况下,您可以将 lambda 分配给一个变量然后应用它,或者如其他答案所述,将其转换为适当的函数类型并应用它。

如果您希望函数应用程序作为采用 lambda 的扩展方法,则必须自己编写。

您在这里有效地指出的是 C# 中缺少“let 表达式”。我真正想在这里写的是:

int value = 
  let o = panel.Controls.Cast<Control>().Last() in 
  o.Location.Y + o.Size.Height;

支持哪些语言,但 C# 只支持查询理解。

有关 C# 中的“let 表达式”以及它们为何有用以及它们如何等效于您的应用程序函数的更多信息,请参阅我对此问题的回答:DRY (Don't Repeat Yourself) and if assignements


顺便说一句,如果您将方法命名为 Select 并创建一个名为 SelectMany 的双应用程序版本,那么您可以在...上使用 LINQ ......好吧,任何东西:

using System;
static class X
{
    public static R Select<A, R>(this A item, Func<A, R> selector) =>
      selector(item);
    public static R SelectMany<A, B, R>(this A item, Func<A, B> toB, Func<A, B, R> toR) =>
      toR(item, toB(item));
}
public class P
{
    public static void Main()
    {
        string myString = "hello";
        string result = from s in myString
                        from b in s + "goodbye"
                        select b.ToUpper() + " " + s;
        Console.WriteLine(result);
    }
}

这是编写简单程序的一种奇怪的巴洛克式的方式,但如果你真的一心想做扩展方法来完成语言中已经存在的事情,那么你可以做到!


练习:实现identity monad。你一路走好!

【讨论】:

  • 哇,好答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-03
  • 2011-11-28
  • 1970-01-01
  • 2023-03-14
  • 2020-12-14
相关资源
最近更新 更多