C# – 冷知识 (新手)

闭包

C# 比较少用匿名函数,所以闭包坑没用像早年 JavaScript 那么明显,但它还是有相同的坑哦。

参考:博客园 小林野夫 – C# 闭包

List<Action> actions = [];
for (var index = 0; index < 3; index++)
{
  actions.Add(() => Console.WriteLine(index));
}
actions.ForEach(action => action()); // 3 ... 3 ... 3

输出的结果是三个 3。

因为匿名函数里的 index 是对外部变量的引用。

当匿名函数被执行时,此时的 index 已经 for loop 结束,变成 3 了。

要避开闭包的坑,可以使用 foreach

List<Action> actions = [];
foreach (var index in Enumerable.Range(0, 3))
{
  actions.Add(() => Console.WriteLine(index));
}
actions.ForEach(action => action()); // 0 ... 1 ... 2

因为 C# 对 foreach 有特殊处理。

或者使用 local variable 把 index 保存起来

List<Action> actions = [];
for (var index = 0; index < 3; index++)
{
  var localIndex = index;
  actions.Add(() => Console.WriteLine(localIndex));
}
actions.ForEach(action => action()); // 0 ... 1 ... 2

 

替 Action/Func Parameter 设置名字

public static void MatchBracket(string value, string bracket, Action<int, int, string> action) { 
    
}

Action/Func 的 parameter 是不可以设置名字的, 只能声明类型, 对调用的人不友好.

Can you name the parameters in a Func<T> type?

有 2 个方法可以让它好一些.

1. 用 delegate 声明

public delegate void Action(int start, int end, string valueInBracket);
public static void MatchBracket(string value, string bracket, Action action)
{

}

虽然在调用的时候依然无法智能提示, 但至少有个地方可以找到.

2. 用 Tuple

public static void MatchBracket1(string value, string bracket, Action<(int Start, int End, string ValueInBracket)> action)
{

}

调用时可以看到提示

缺点就是结构换了. 可能不习惯.

使用的时候要解构 (而且不能直接在 parameter 里解, 要拿出来才能解), 或者干脆把它当对象用会更好一些.

MatchBracket1(value, "{}", matchInfo =>
{
    var (start, end, valueInBracket) = matchInfo;
});

 它也不支持写 params 

 

隐式类型转换

有一个 class Person

public class Person
{

}

我们尝试拿一个 int 强转到 Person 

它会直接报错。

加上隐式类型转换操作符

public class Person
{
  public static implicit operator Person(int number)
  {
    return new Person();
  }
}

这样就不会再报错

逻辑很简单,当强转的时候,操作符方法会被调用,参数是 55,然后返回一个 Person 实例。

日常例子

[ApiController]
[Route("api")]
public class PersonController(
  ApplicationDbContext db
) : ControllerBase
{
  [ODataAttributeRouting]
  [EnableQuery]
  [HttpGet("people")]
  [Produces(MediaTypeNames.Application.Json)]
  [ProducesResponseType(StatusCodes.Status200OK)]
  public ActionResult<IQueryable<Person>> GetPeople()
  {
    return db.People;
  }
}

GetPeople 方法要求返回 ActionResult 实例,但是代码最终返回的是 IQueryable,结果没有报错。

原因就是 ActionResult 内部实现了隐式类型转换操作符

 

posted @ 2022-01-03 11:26  兴杰  阅读(103)  评论(0)    收藏  举报