C#函数编写

时间:2015-06-16 17:58:18

标签: c#

在C#中编写代码的简单问题。以这种方式编写函数是否有任何价值

Func<int, int> add2 = x => x + 2;

相比
int add2(int x)
{
    return x + 2;
}

我发现第二个例子更容易阅读,或者我错过了为什么第一个函数以这种方式编写的非常具体的原因?

6 个答案:

答案 0 :(得分:2)

第一个是Function对象,而第二个只是一个函数/方法。区别在于第一个是.Net平台的Object hirachy中的一个对象,而后一个只是一个不在那个hirachy中的函数。但据我所知,C#在第二种情况下为你做了自动装箱,无论何时你需要自动装箱。另一个区别可能是反射api中这些函数的出现。虽然第一个只是一个带有函数值的变量,但第二个实际上是api中的一个函数。

答案 1 :(得分:1)

第一个可以被认为等同于功能指针,只需等待接收&#34;委托&#34;就可以为空。在您出现的情况下,其实现已使用 lambda表达式进行声明。第二个是一个实际的常规函数​​,它也可以作为参数传递给具有等效模板类型的Func

int add2(int x)
{
    return x + 2;
}

int main()
{   
    Func<int, int> add2Func = add2;
    // Invoking:
    int retVal = add2Func.Invoke(10); // will call the add2 function
}

答案 2 :(得分:1)

这两个代码(假设它们都在方法之外)的含义不同。第一个声明了Func<int, int>类型的字段(一个委托取int并返回int),并为其指定一个lambda表达式。第二个只用身体声明一个普通的方法。区别在于(除了第一个是字段,第二个是方法),您可以更改add2的值,如果它是一个字段。

Lambda表达式很有用,因为它们编写起来很短,使用类型推断来确定参数的类型(x),如果没有指定为块,则隐式返回一个值。这就是为什么它们主要用在LINQ中,因为它们可以很好地适用于一行。还有第二种语法,匿名方法,如下所示:

Func<int, int> add2 = delegate(int x)
{
    return x + 2;
};

匿名方法的优点是您可以省略参数(delegate{ ... }),并且它匹配任何委托类型。

答案 3 :(得分:1)

是的,正如其他人所说,严格地说,你在谈论两件不同的事情。但是你的问题仍然是相关的,在不同的情况下,你经常面临必须在使用lambda编写代码或编写正式方法之间做出选择。

选择主要是风格问题。我会说,有时候,有些人过度使用lambda风格只是因为他们试图变得聪明,并希望看到他们可以在尽可能小的空间内放入多少代码。但它肯定会变得更难阅读。

然而,肯定存在使用lambdas会提高可读性和便利性的情况。

一个很好的例子就是将它与可链接的LINQ方法一起使用。

您觉得哪个版本更具可读性和便利性?

Lambda版本

List<int> list = new List<int> { 6, 3, 9, 2, 6, 9, 7, 11, -4 };
IEnumerable<int> processedList = list.Where(i => i % 2 == 0).OrderBy(i => i);

正常方法版本

List<int> list = new List<int> { 6, 3, 9, 2, 6, 9, 7, 11, -4 };
IEnumerable<int> processedList = list.Where(FilterByNumber).OrderBy(OrderByNumber);

private bool FilterByNumber(int i)
{
    return i % 2 == 0;
}

private int OrderByNumber(int i)
{
    return i;
}

做一些你感觉更舒服的事情,当你面对以后不得不改变代码时,不会让你感到头疼。

答案 4 :(得分:1)

创建等效表达式的方法:

Func<int, int> add2 = x => x + 2;

要做的事:

Expression<Func<int, int>> add2 = x => x + 2;

实际上,我们甚至不需要知道代码中发生了什么:

something.Select(x => x + 2);

编译器根据哪个更有用创建一个委托(按照第一个)或表达式(按照第二个)。 (很可能somethingIQueryable<int>,在这种情况下将生成表达式,或者其他类型的IEnumerable<int>,在这种情况下将生成Func

创建等效表达式的方法:

int add2(int x)
{
  return x + 2;
}

(也就是说,避免使用lambda语法)是:

var parExp = Expression.Parameter(typeof(int), "x");
var lamda = Expression.Lambda<Func<int, int>>(
    Expression.Add(parExp, Expression.Constant(2, typeof(int))), parExp);

与此相比,我会说x => x + 2更具可读性。

有一些不太极端的情况,lambda语法可以立即更易读,特殊情况是你连续使用几个,就像Linq经常发生的情况一样,但在很多其他情况下也是如此。

它还增加了整个应用程序的可读性,像add2这样的小简单方法只在使用*时定义,而不是用很多这样的方法填充类。考虑这个稍微修改过的现实代码:

var lastModAndTag = await ctx.Pending
  .Select(pn => new {pn.Modified, pn.Tag})
  .Union(ctx.Preferences.Select(pr => new {pr.Modified, pr.Tag}))
  .OrderByDescending(dt => dt.Modified).FirstOrDefaultAsync();

以下内容:

private class DateAndTag // Can't use anonymous types as we have to pass method boundaries.
{
  public DateTime Modified { get; set; }
  public string Modified { get; set; }
}
private static DateAndTagFromPending(Pending pn)
{
  return new DateAndTag{Modified = pn.Modified, Tag = pn.Tag};
}
private static DateAndTagFromPreference(Preference pr)
{
  return new DateAndTag{Modified = pr.Modified, Tag = pr.Tag};
}
private static DateTime GetModified(DateAndTag dt)
{ //it's possible to get the property into a method or delegate with reflection, but that's even worse.
  return dt.Modified;
}
//and now in the calling code;
var lastModAndTag = ctx.Pending
  .Select(DateAndTagFromPending)
  .Union(ctx.Preferences.Select(DateAndTagFromPreference))
  .OrderByDescending(GetModified).FirstOrDefault();

这真的不容易阅读。而且我们也无法使用Async,因为这只适用于表达式。实际上,我们被迫从数据库中获取所有内容来运行这些函数。但即使我们不关心(因为我们在内存中工作并使用Func),它仍然需要更多的代码。

最后,如果您需要咖喱或以其他方式捕获变量,那么您的选择实际上是在:

之间
Func<int, int> addX = val => val + x; // x is from outside of this step.

Func<int, int> addX = delegate(int val)
{
  return val + x;
};

如果你不习惯lambda形式,那么你现在可能看起来更具可读性,但对我来说当然没有。

事实上,越是习惯了lambda形式,就越会使用它只是因为它看起来更具可读性。

如果你有一个复杂的方法,是递归的,需要是一个可访问的方法,那么lambda表格将没有那么有用

*虽然请注意,如果您有两个与add2相同的方法,编译器通常会将它们合并到生成的程序集中的单个委托中,因此源中的这种重复不会浪费或需要担心。< / p>

答案 5 :(得分:0)

有时使用Func<T2,T2,...,Tn>代替静态函数声明会更好。

示例:您有一个将对象从Type1转换为Type2

的通用方法
public Tout Convert<Tin, Tout>(Tin obj, Func<Tin, Tout> convertFunc)
{
   return convertFunc(obj);
}

...

Class1 obj = new Class1();
Class2 convertedObject = Convert<Class1, Class2>(obj, x => new Class2());

有什么好处? Lambda表达式更短,代码更易读。