文章目录
- 一、匿名函数(`delegate` )
- 1、什么是匿名函数?
- 2、匿名函数的基本语法
- 2.1 语法
- 2.2 **没有参数的匿名函数:**
- 2.3 **有参数的匿名函数:**
- 2.4 **有返回值的匿名函数:**
- 3、匿名函数的使用示例
- 3.1 作为参数传递匿名函数
- 3.2 作为返回值返回匿名函数
- 4、匿名函数的缺点
- 4.1 无法单独移除
- 4.2 匿名函数难以调试
- 4.3 性能开销
- 5. 匿名函数与闭包
- 6、匿名函数的总结
- 二、Lambda表达式
- 1 、什么是 Lambda 表达式
- 2、Lambda 表达式的语法
- 3、匿名函数转Lambda 表达式示例
- 4、Lambda 表达式的使用
- 4.1 **无参无返回值**:
- 4.2 **有参的无返回值**:
- 4.3 **有返回值**:
- 5、闭包
- 5.1 概念
- 5.2 示例
- 5.3 如何避免这种问题?
- 5、总结
- 6、注意:
- 三、委托和匿名函数的区别
- 专栏推荐
- 完结
一、匿名函数(delegate
)
1、什么是匿名函数?
匿名函数是没有名字的函数
。它通常用于在需要传递函数或行为时,避免了定义一个额外的命名函数。匿名函数最常用的场景是在委托(delegate
)和事件(event
)的处理过程中。
常见使用场景:
- 委托传递时
- 事件绑定时
2、匿名函数的基本语法
2.1 语法
匿名函数使用delegate
关键字来声明,语法如下:
delegate (参数列表) { 函数逻辑 };
2.2 没有参数的匿名函数:
Action a = delegate {Console.WriteLine("匿名函数逻辑");
};
a();
2.3 有参数的匿名函数:
Action<int, string> b = delegate (int a, string b) {Console.WriteLine(a);Console.WriteLine(b);
};
b(108, "123");
2.4 有返回值的匿名函数:
Func<string> c = delegate {return "返回的字符串";
};
Console.WriteLine(c());
3、匿名函数的使用示例
3.1 作为参数传递匿名函数
可以将匿名函数作为参数传递给方法:
public void DoSomething(int a, Action fun)
{Console.WriteLine(a);fun(); // 调用传入的匿名函数
}// 调用方法时传递匿名函数
DoSomething(10, delegate {Console.WriteLine("传入的匿名函数逻辑");
});
3.2 作为返回值返回匿名函数
匿名函数可以作为方法的返回值:
public Action GetFun()
{return delegate {Console.WriteLine("匿名函数返回值");};
}Action action = GetFun();
action();
直接调用返回的委托函数
GetFun()(); // 直接调用返回的匿名函数
4、匿名函数的缺点
4.1 无法单独移除
匿名函数没有名称,因此无法通过直接引用来移除它们。尤其在委托或事件中,当添加多个匿名函数时,无法移除某一个匿名函数。
例如:
Action ac = delegate {Console.WriteLine("匿名函数一");
};ac += delegate {Console.WriteLine("匿名函数二");
};ac(); // 会打印 "匿名函数一" 和 "匿名函数二"ac -= delegate {Console.WriteLine("匿名函数二");
}; // 编译错误:不能移除匿名函数ac(); // 依然打印 "匿名函数一" 和 "匿名函数二"
由于匿名函数没有名称,无法通过-=
运算符去移除某一个特定的匿名函数。
4.2 匿名函数难以调试
因为匿名函数没有名称,所以它们难以调试。无法直接断点到匿名函数内,也无法追踪其调用栈。
4.3 性能开销
在某些情况下,匿名函数会增加额外的闭包和委托创建的性能开销,尤其是当它们在频繁调用的场景中使用时。
5. 匿名函数与闭包
匿名函数在使用时可以捕获外部变量(即使外部变量在匿名函数外部的作用域中已经超出了生命周期),这被称为闭包。
例如:
int x = 10;
Action action = delegate {Console.WriteLine(x); // 捕获了外部变量x
};
x = 20;
action(); // 打印 20
这个例子中,匿名函数捕获了外部变量x
,并且打印了x
的当前值。由于匿名函数形成闭包,它保存了对x
的引用,即使x
的值被修改,匿名函数依然会使用最新的值。
6、匿名函数的总结
- 定义简洁:匿名函数没有名称,可以直接在需要的地方声明并使用,适用于委托和事件的传递。
- 用途广泛:尤其在回调函数、事件处理、LINQ表达式等场景中非常常见。
- 缺点:无法移除、调试困难、性能开销较大等。
- 闭包特性:匿名函数可以捕获外部变量(闭包),这使得它们能够访问外部作用域的变量。
二、Lambda表达式
1 、什么是 Lambda 表达式
Lambda 表达式可以被看作是匿名函数的简写
。它与匿名函数的区别在于,写法更为简洁,且与委托或者事件配合使用的方式相同。
- 匿名函数:没有名称的函数,通常用
delegate
关键字定义。 - Lambda 表达式:更简洁的函数表示法,使用
=>
符号。
2、Lambda 表达式的语法
Lambda 表达式的语法格式如下:
(parameters) => expression_or_statement_block
- 参数列表:Lambda 表达式的输入参数,可以省略类型,C# 会根据上下文推断类型。
- 表达式或语句块:Lambda 表达式的执行代码。如果是单个表达式,返回值会被自动推断。如果是多行代码,必须用
{}
包裹。
3、匿名函数转Lambda 表达式示例
-
匿名方法(使用
delegate
):delegate void MyDelegate(int x); MyDelegate d = delegate(int x) { Console.WriteLine(x); }; d(10);
-
Lambda 表达式:
Action<int> d = (int x) => { Console.WriteLine(x); }; d(10);
4、Lambda 表达式的使用
4.1 无参无返回值:
Action a = () => Console.WriteLine("无参无返回值的 Lambda 表达式");
a();
4.2 有参的无返回值:
Action<int> a2 = (int value) => Console.WriteLine($"有参数的 Lambda 表达式 {value}");
a2(100);
可以省略参数类型
Lambda 表达式的参数类型可以根据委托类型自动推断,所以可以省略参数类型。
Action<int> a3 = (value) => Console.WriteLine($"省略参数类型的写法 {value}");
a3(200);
4.3 有返回值:
使用 Func
委托时,Lambda 表达式可以有返回值。
Func<string, int> a4 = (value) =>
{Console.WriteLine($"有返回值的 Lambda 表达式 {value}");return 1;
};
Console.WriteLine(a4("123123"));
5、闭包
5.1 概念
- 闭包 是 Lambda 表达式捕获外部作用域中的变量的机制。
- Lambda 表达式不仅仅捕获变量的值,而是捕获了变量的引用。它可以在 Lambda 表达式的执行时继续访问这些变量,即使这些变量所在的作用域已经结束。
5.2 示例
闭包常常会在循环中表现出意外的行为,因为 Lambda 表达式捕获了循环变量的引用,而不是它的值。以下是一个例子:
public class ClosureInLoop
{public void Test(){List<Action> actions = new List<Action>();for (int i = 0; i < 5; i++){// Lambda 表达式捕获了循环变量 i 的引用actions.Add(() => Console.WriteLine(i));}// 输出的结果是 5 次 5,因为 Lambda 捕获的是 i 的引用,而不是它的值foreach (var action in actions){action();}}
}
解释:
在上面的代码中,我们将 Lambda 表达式添加到一个列表中。每个 Lambda 表达式都捕获了循环变量 i 的引用。因为 Lambda 表达式没有立即执行,而是在循环结束后才被执行,所以它们访问的是最终的 i 值(即 5)。所有的 Lambda 表达式都会输出 5,而不是它们在创建时的 i 值。
5.3 如何避免这种问题?
如果你希望 Lambda 表达式捕获的是 i 在每次迭代时的值,而不是循环结束后的值,你可以在 Lambda 表达式中引入一个局部变量来保存当前的 i 值:
public class ClosureInLoopFix
{public void Test(){List<Action> actions = new List<Action>();for (int i = 0; i < 5; i++){int capturedValue = i; // 将 i 的值保存到一个局部变量actions.Add(() => Console.WriteLine(capturedValue));}foreach (var action in actions){action(); // 现在会输出 0, 1, 2, 3, 4}}
}
在这种情况下,capturedValue 是在每次循环时创建的一个新的局部变量,这样每个 Lambda 表达式都会捕获不同的 capturedValue,而不是 i 的引用。
5、总结
- Lambda 表达式 是匿名函数的简化写法,通常用于委托或事件处理。
- 语法:
(参数列表) => 表达式/语句块
。 - 闭包:Lambda 表达式可以捕获外部函数的局部变量,并且可以在外部函数已经结束后依然访问这些变量。
6、注意:
- Lambda 表达式使代码更简洁,但缺点是无法明确地移除或管理。
- 闭包 可能导致意外的引用或性能问题,特别是在循环或多次调用 Lambda 时。
三、委托和匿名函数的区别
C# 中的匿名函数和委托确实有一些相似之处,尤其是在代码外观上可能会让人觉得它们很像,但它们并不是同一个概念。
委托
是一种类型
,它定义了方法的签名,并可以引用任何符合该签名的方法。匿名函数
是没有显式名称的函数
,它们可以在声明时立即定义行为,包括匿名方法和 Lambda 表达式。委托和匿名函数一起使用
:匿名函数可以被赋值给委托类型的变量,从而实现更灵活和简洁的编程风格。delegate
关键字 主要用于定义委托类型,但在匿名方法中也会出现,用于直接定义函数体。Lambda 表达式则不需要显式的 delegate 关键字。
专栏推荐
地址 |
---|
【从零开始入门unity游戏开发之——C#篇】 |
【从零开始入门unity游戏开发之——unity篇】 |
【制作100个Unity游戏】 |
【推荐100个unity插件】 |
【实现100个unity特效】 |
【unity框架开发】 |
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~