IEnumerable.Select()表示单个项目

时间:2018-06-13 12:56:39

标签: c#

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

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函数代替吗?

3 个答案:

答案 0 :(得分:5)

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

嗯,你可以这样做:

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;

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

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

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

答案 1 :(得分:5)

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

鉴于原始代码:

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

答案 2 :(得分:4)

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

r = F(a)

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

如果你想将函数应用程序作为一个带lambda的扩展方法,你必须自己编写它。

你在这里有效地提到的是C#中缺少“let expressions”。我真正想写的是:

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

支持哪种语言,但C#仅支持查询理解。

请参阅我对这个问题的回答,了解更多关于C#中的“let expressions”及其有用的原因,以及它们与您的应用程序函数的对应关系: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);
    }
}

这是编写简单程序的一种奇怪的巴洛克式方式,但是如果你真的倾向于使用已经使用该语言的东西来制作扩展方法,那么你就可以做到!

练习:实施身份monad 。你很顺利!