Skip to content

Commit b886c99

Browse files
author
wangb
committed
add posts
1 parent ee27629 commit b886c99

8 files changed

+388
-230
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
title: "C# 如何在字符串中查找大小写不敏感子字符串"
3+
date: 2021-09-22T13:24:20+08:00
4+
description: "在 C# 中如何查找大小写不敏感的子字符串"
5+
tags: ["C#"]
6+
categories: ["C# 语言"]
7+
---
8+
9+
在 C# 中如何查找大小写不敏感的子字符串, 比如我们想要下面的例子返回 `true`:
10+
11+
```c#
12+
string title = "ASTRINGTOTEST";
13+
title.Contains("string") == true;
14+
```
15+
<!-- more -->
16+
17+
## 对于英语
18+
19+
一种解决方案是将两个字符串都转换成大写或小写, 然后再进行比较:
20+
21+
```c#
22+
title.ToUpper().Contains("string".ToUpper()) == true;
23+
```
24+
25+
另一种方案是使用 [`IndexOf` 方法](https://msdn.microsoft.com/en-us/library/ms224425(v=vs.110).aspx), 该方法有一个重载方法可以忽略字符串的大小写, 即 [`StringComparison.OrdinalIgnoreCase`](https://msdn.microsoft.com/en-us/library/system.stringcomparer.ordinalignorecase(v=vs.110).aspx) 作为第二个参数:
26+
27+
```c#
28+
string title = "STRING";
29+
bool contains = title.IndexOf("string", StringComparison.OrdinalIgnoreCase) >= 0;
30+
```
31+
32+
我们还可以为 `String` 定义一个扩展方法, 重载 `Contains`
33+
34+
```c#
35+
public static class StringExtensions
36+
{
37+
public static bool Contains(this string source, string toCheck, StringComparison comp)
38+
{
39+
return source?.IndexOf(toCheck, comp) >= 0;
40+
}
41+
}
42+
```
43+
44+
使用方法:
45+
46+
```c#
47+
string title = "STRING";
48+
bool contains = title.Contains("string", StringComparison.OrdinalIgnoreCase);
49+
```
50+
51+
## 非英语语言解决方案
52+
不同语言对于大小写的定义是不一样的, 比如在英语中 `I``i` 是第九个字符的大小写形式, 然而在土耳其语中, 这两个字符分别是[第 19 和 20 个字符](http://en.wikipedia.org/wiki/Dotted_and_dotless_I), 土耳其语中 `i` 的大写形式为 `İ`. 因此 `tin``TIN` 在英语中是同一个单词, 在土耳其语中前一个词表示 `精神`, 第二个是一个拟声词.
53+
54+
因此上面提供的方法只适用于英语语言, 为了兼容各种不同语言, C# 中提供了 [`CultureInfo`](http://msdn.microsoft.com/en-gb/library/system.globalization.cultureinfo(v=vs.110).aspx) API, 我们可以通过下面方法进行判断:
55+
56+
```c#
57+
culture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0
58+
```
59+
60+
其中 `culture` 是一个 `CultureInfo` 对象.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
title: "C# 获取运行时内存占用"
3+
date: 2021-09-28T14:22:18+08:00
4+
description: "C# 获取运行时内存占用"
5+
tags: ["C#", "内存占用"]
6+
categories: ["C#"]
7+
---
8+
9+
## 获取 GC 分配的内存
10+
11+
这里输出的是 `GC` 管理的堆上内存.
12+
13+
```c#
14+
Console.WriteLine("Total Memory: {0}", GC.GetTotalMemory(false));
15+
```
16+
<!-- more -->
17+
18+
## 获取进程分配的所有内存
19+
20+
这里输出的是进程从操作系统申请的所有内存, 包括堆, 栈, 静态变量, 本地库申请的内存等, .
21+
22+
```c#
23+
Process proc = Process.GetCurrentProcess();
24+
System.Console.WriteLine($"Current Memory Usage: {proc.PrivateMemorySize64}");
25+
```
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
title: "C# 语言 String 和 string 的区别"
3+
date: 2021-09-22T13:24:20+08:00
4+
description: "C# 中 String 和 string 类型的区别和适合的使用方式"
5+
tags: ["C#"]
6+
categories: ["C# 语言"]
7+
---
8+
9+
10+
在 C# 语言中 `string``System.String` 的别名. 因此从技术上来说, 这两者没有任何区别, 就像 `int``System.Int32`.
11+
<!-- more -->
12+
13+
通常我们推荐使用 `string` 类型声明一个对象.
14+
15+
```c#
16+
string place = "world";
17+
```
18+
19+
同时, 推荐使用 `String` 来引用字符串类型中的方法, 属性等, 比如:
20+
21+
```c#
22+
string greet = String.Format("Hello {0}!", place);
23+
```
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
title: "C# 中如何遍历 Enum 枚举值"
3+
date: 2021-09-22T15:47:20+08:00
4+
description: "在 C# 中遍历 Enum 枚举值"
5+
tags: ["C#"]
6+
categories: ["C# 语言"]
7+
---
8+
9+
假如我们有如下 `Shape` 枚举类型, 我们要遍历其中所有的枚举值
10+
```c#
11+
public enum Shape
12+
{
13+
Circle,
14+
Triangle,
15+
Rect
16+
}
17+
```
18+
19+
可以通过 [`GetValues`](https://docs.microsoft.com/en-us/dotnet/api/system.enum.getvalues) 方法实现:
20+
<!-- more -->
21+
22+
```c#
23+
var shapes = Enum.GetValues(typeof(Shape));
24+
25+
foreach (var shape in shapes)
26+
{
27+
DoSomething(shape);
28+
}
29+
```
30+
31+
另外我们还可以做一点封装
32+
33+
```c#
34+
public static class EnumUtil {
35+
public static IEnumerable<T> GetValues<T>() {
36+
return Enum.GetValues(typeof(T)).Cast<T>();
37+
}
38+
}
39+
40+
var shapes = EnumUtil.GetValues<Shape>();
41+
```
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
title: "ASP.NET 中 MapControllerRoute, MapDefaultControllerRoute 和 MapControllers 的区别"
3+
date: 2021-09-24T12:52:02+08:00
4+
description: "ASP.NET 中 MapControllerRoute, MapDefaultControllerRoute 和 MapControllers 的区别"
5+
tags: ["ASP.NET", "路由"]
6+
categories: ["ASP.NET"]
7+
---
8+
9+
`MapControllerRoute` 定义了默认的 `Controller` 的路由映射关系, 就像我们在大部分的教程中看到的:
10+
11+
```c#
12+
app.MapControllerRoute(
13+
name: "default",
14+
pattern: "{controller=Home}/{action=Index}/{id?}");
15+
```
16+
17+
`MapDefaultControllerRoute` 是上面代码的缩写形式, 也就是 `pattern` 等于 `{controller=Home}/{action=Index}/{id?}`.
18+
19+
`MapControllers` 没有定义默认的 `Controller` 的路由, 我们需要在 `Controller``Action` 上使用 `[Route]` 来定义对应的路由, 大部分时候在 `ApiController` 中使用.
20+
<!-- more -->
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
---
2+
title: "在 .NET 中运行后台任务"
3+
date: 2021-09-24T09:36:50+08:00
4+
description: "在 .NET 中运行后台任务"
5+
tags: [".NET", "后台任务", "定时任务", "队列任务", "Generic Host"]
6+
categories: [".NET", "ASP.NET"]
7+
---
8+
9+
在一个 `ASP.NET` 程序中我们常常需要进行比较耗时间的任务, 通常我们不会在一个请求中等待这些任务完成后再返回响应, 比如发邮件, 短信等. 这时候就需要我们将任务放到后台运行, 然后直接返回响应, 告诉用户任务已经再进行了. 本文将教会大家如何在 `.NET` 中运行后台任务, 示例代码请点击[链接](https://github.com/wangbinyq/net-background-task-example)
10+
<!-- more -->
11+
12+
## 创建 `Worker` 程序
13+
14+
`.NET` 自带了一个后台任务模板, 我们执行 `dotnet new worker -o BackgroundTask` 就可以创建一个通用的 `console` 后台任务程序. 该模板中包含一个 `Worker` 类型, 继承了 `BackgroundService`, 然后在 `HostBuilder` 中通过 `AddHostedService` 注册. 最后调用 `host.RunAsync()` 执行该后台任务. 该示例程序每隔一秒钟打印当前时间.
15+
16+
```c#
17+
namespace BackgroundTask;
18+
19+
public class Worker : BackgroundService
20+
{
21+
private readonly ILogger<Worker> _logger;
22+
23+
public Worker(ILogger<Worker> logger)
24+
{
25+
_logger = logger;
26+
}
27+
28+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
29+
{
30+
while (!stoppingToken.IsCancellationRequested)
31+
{
32+
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
33+
await Task.Delay(1000, stoppingToken);
34+
}
35+
}
36+
}
37+
```
38+
39+
```c#
40+
using BackgroundTask;
41+
42+
IHost host = Host.CreateDefaultBuilder(args)
43+
.ConfigureServices(services =>
44+
{
45+
services.AddHostedService<Worker>();
46+
})
47+
.Build();
48+
49+
await host.RunAsync();
50+
```
51+
52+
`AddHostedService` 接受一个 `IHostedService` 的泛型参数, 其有两个方法 `StartAsync``StopAsync`. 一个 `IHostedService` 跟一个单例 `Service` 相似, 只是启动时会调用 `StartAsync` 方法, 结束时调用 `StopAsync` 方法.
53+
`BackgroundService` 实际上是一个 `IHostedService` 长时间运行的实现.
54+
55+
现在我们添加一个 `SimpleWorker` 类:
56+
57+
```c#
58+
namespace BackgroundTask;
59+
60+
public class SimpleWorker : IHostedService
61+
{
62+
private ILogger<SimpleWorker> _logger;
63+
64+
public SimpleWorker(ILogger<SimpleWorker> logger)
65+
{
66+
_logger = logger;
67+
}
68+
69+
public Task StartAsync(CancellationToken cancellationToken)
70+
{
71+
_logger.LogInformation("Simple Worker Start");
72+
return Task.CompletedTask;
73+
}
74+
75+
public Task StopAsync(CancellationToken cancellationToken)
76+
{
77+
_logger.LogInformation("Simple Worker Stop");
78+
return Task.CompletedTask;
79+
}
80+
}
81+
```
82+
我们还需要在 `Program.cs` 中添加注册.
83+
84+
```c#
85+
services.AddHostedService<SimpleWorker>();
86+
```
87+
88+
现在在启动和结束时会分别打印信息.
89+
90+
![打印信息](/images/background-task/task-1.png)
91+
92+
93+
## 使用 `Timer` 进行定时任务
94+
95+
默认的 `Worker` 实现定时每秒钟打印执行的效果, 我们也可以使用 `Timer` 可以获得同样的效果.
96+
97+
```c#
98+
namespace BackgroundTask;
99+
100+
public class TimerWorker : IHostedService
101+
{
102+
private int _count = 0;
103+
private ILogger<TimerWorker> _logger;
104+
private Timer? _timer;
105+
106+
public TimerWorker(ILogger<TimerWorker> logger)
107+
{
108+
_logger = logger;
109+
}
110+
111+
public Task StartAsync(CancellationToken cancellationToken)
112+
{
113+
_logger.LogInformation("Timer Worker Start");
114+
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
115+
116+
return Task.CompletedTask;
117+
}
118+
119+
public Task StopAsync(CancellationToken cancellationToken)
120+
{
121+
_logger.LogInformation("Timer Worker Stop");
122+
123+
return Task.CompletedTask;
124+
}
125+
126+
private void DoWork(object? state)
127+
{
128+
var count = Interlocked.Increment(ref _count);
129+
130+
_logger.LogInformation($"count = {count}, time: {DateTimeOffset.Now}");
131+
}
132+
}
133+
```
134+
135+
同时别忘了注册该 `Service`.
136+
137+
![定时任务](/images/background-task/task-2.png)
138+
139+
## 使用 `Channel` 创建任务队列
140+
141+
更常见的使用后台任务的方式是使用队列, `.NET` 为我们提供了 `System.Threading.Channel` 类. 这里我们需要两个 `Worker`: 一个作为生产者, 一个作为消费者:
142+
143+
```c#
144+
namespace BackgroundTask;
145+
146+
using System.Threading.Channels;
147+
148+
public class ProducerWorker : BackgroundService
149+
{
150+
private int _count = 0;
151+
private Channel<int> _queue;
152+
153+
public ProducerWorker(Channel<int> queue)
154+
{
155+
_queue = queue;
156+
}
157+
158+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
159+
{
160+
while (!stoppingToken.IsCancellationRequested)
161+
{
162+
var count = Interlocked.Increment(ref _count);
163+
await _queue.Writer.WriteAsync(count, stoppingToken);
164+
await Task.Delay(1000, stoppingToken);
165+
}
166+
}
167+
}
168+
```
169+
170+
```c#
171+
namespace BackgroundTask;
172+
173+
using System.Threading;
174+
using System.Threading.Channels;
175+
using System.Threading.Tasks;
176+
177+
public class ConsumerWorker : BackgroundService
178+
{
179+
private Channel<int> _queue;
180+
private ILogger<ConsumerWorker> _logger;
181+
182+
public ConsumerWorker(Channel<int> queue, ILogger<ConsumerWorker> logger)
183+
{
184+
_queue = queue;
185+
_logger = logger;
186+
}
187+
188+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
189+
{
190+
while (!stoppingToken.IsCancellationRequested)
191+
{
192+
var count = await _queue.Reader.ReadAsync(stoppingToken);
193+
194+
_logger.LogInformation($"do work for: {count}");
195+
}
196+
}
197+
}
198+
```
199+
200+
同时我们需要通过依赖注入注入一个 `Channel<int>` 的单例:
201+
202+
```c#
203+
var queue = Channel.CreateBounded<int>(10);
204+
services.AddSingleton(queue);
205+
services.AddHostedService<ProducerWorker>();
206+
services.AddHostedService<ConsumerWorker>();
207+
```
208+
209+
![队列任务](/images/background-task/task-3.png)

0 commit comments

Comments
 (0)