返回提交历史
Added
README.md
+500
-0
Added
XFEExtension.NetCore.AutoConfig.Analyzer/AnalyzerReleases.Shipped.md
+8
-0
Added
XFEExtension.NetCore.AutoConfig.Analyzer/AnalyzerReleases.Unshipped.md
+9
-0
Added
XFEExtension.NetCore.AutoConfig.Analyzer/CodeFix/AutoConfigCodeFixProvider.cs
+62
-0
Added
XFEExtension.NetCore.AutoConfig.Analyzer/Diagnostics/AutoConfigDiagnostics.cs
+127
-0
Added
XFEExtension.NetCore.AutoConfig.Analyzer/Generator/ProfilePropertyAutoGenerator.cs
+293
-0
Added
XFEExtension.NetCore.AutoConfig.Analyzer/Generator/ProfilePropertySyntaxReceiver.cs
+23
-0
Modified
XFEExtension.NetCore.AutoConfig.Analyzer/XFEExtension.NetCore.AutoConfig.Analyzer.csproj
+14
-3
Modified
XFEExtension.NetCore.AutoConfig.sln
+3
-0
Added
XFEExtension.NetCore.AutoConfig/AutoLoadProfileAttribute.cs
+14
-0
Added
XFEExtension.NetCore.AutoConfig/ProfileEntryInfo.cs
+20
-0
Added
XFEExtension.NetCore.AutoConfig/ProfileFieldAutoGenerateAttribute.cs
+9
-0
Added
XFEExtension.NetCore.AutoConfig/ProfileInfo.cs
+62
-0
Added
XFEExtension.NetCore.AutoConfig/ProfileInstanceAttribute.cs
+7
-0
Added
XFEExtension.NetCore.AutoConfig/ProfilePropertyAddGetAttribute.cs
+14
-0
Added
XFEExtension.NetCore.AutoConfig/ProfilePropertyAddSetAttribute.cs
+14
-0
Added
XFEExtension.NetCore.AutoConfig/ProfilePropertyAttribute.cs
+25
-0
Modified
XFEExtension.NetCore.AutoConfig/XFEExtension.NetCore.AutoConfig.csproj
+21
-0
Added
XFEExtension.NetCore.AutoConfig/XFEProfile.cs
+303
-0
Added
logoIcon.png
+0
-0
XFEstudio/XFEExtension.NetCore.AutoConfig
从XFEExtension.NetCore中迁移
f8dfe13
代码差异
20 个文件
+1528
-3
@@ -0,0 +1,500 @@
1
# XFEExtension (XFEExtension)
2
3
## 描述
4
5
XFEExtension是一个C#的DLL库,旨在优化C#代码中常用语句的使用,并提供更简洁的访问方式,快速搭建服务器/客户端,免费ChatGPTAPI接口,免费通讯服务器,XFE下载器,新增格式等
6
7
## 用途
8
9
XFEExtension库适用于各种C#项目,特别适合在需要提高代码可读性的情况下使用。它包含了许多常见操作的拓展方法,使得代码编写更加高效和简便。以下是一些XFEExtension的用途示例:
10
11
- **简化代码访问:** XFEExtension提供了更简洁的语法,使得代码中的访问操作更加清晰和易读。
12
13
- **优化性能:** 通过使用XFEExtension,您可以执行各种性能优化操作,提高应用程序的效率。
14
15
- **加速开发:** 通过减少样板代码,XFEExtension可以加速项目的开发过程,同时提高代码的可维护性。
16
17
---
18
19
## 设置csproj文件配置
20
21
```xml
22
<PropertyGroup>
23
<!--设置是否启用自动配置文件-->
24
<AutoConfig>true</AutoConfig>
25
<!--设置是否启用自动路径-->
26
<AutoPath>true</AutoPath>
27
<!--设置是否启用TODO待办任务提醒-->
28
<TodoList>true</TodoList>
29
<!--设置待办任务的提示级别-->
30
<TodoListWarningLevel>3</TodoListWarningLevel>
31
</PropertyGroup>
32
```
33
34
---
35
36
# 示例(使用前记得进行相应的引用)
37
38
---
39
40
## TODO待办任务提醒
41
42
```csharp
43
//TODO: 这是一个待办任务,使用默认提示级别
44
45
//TODO:1 这是一个待办任务,使用提示级别
46
47
//TODO:3 这是一个待办任务,使用错误提示级别
48
49
//提示级别:0-隐藏,1-提示,2-警告,3-错误
50
```
51
52
---
53
54
## 自动实现配置文件的存储
55
56
#### 基础用法
57
58
```csharp
59
//创建配置文件类
60
partial class SystemConfig
61
{
62
[ConfigProperty]
63
string name;
64
65
[ConfigProperty]
66
int _age;
67
}
68
69
//使用配置文件
70
class Program
71
{
72
static void Main(string[] args)
73
{
74
SystemConfig.Name = "Test";//在设置值的时候会自动记录并储存
75
//SystemConfig.Age = 1;
76
Console.WriteLine(SystemConfig.Name);
77
Console.WriteLine(SystemConfig.Age);//下次打开程序会自动读取上次程序退出时储存的值
78
}
79
}
80
```
81
82
#### 设置get和set方法
83
84
```csharp
85
partial class SystemConfig
86
{
87
[ConfigProperty]
88
[ConfigPropertyAddGet(@"Console.WriteLine(""获取了Name"")")]
89
[ConfigPropertyAddGet("return Current.name")]
90
[ConfigPropertyAddSet(@"Console.WriteLine(""设置了Name"")")]
91
[ConfigPropertyAddSet("Current.name = value")]
92
string name = string.Empty;
93
94
[ConfigProperty]
95
[ConfigPropertyAddGet(@"Console.WriteLine(""获取了Age"")")]
96
[ConfigPropertyAddGet("return Current._age")]
97
[ConfigPropertyAddSet(@"Console.WriteLine(""设置了Age"")")]
98
[ConfigPropertyAddSet("Current._age = value")]
99
int _age;
100
}
101
```
102
103
#### 设置初始值
104
105
```csharp
106
partial class SystemConfig
107
{
108
[ConfigProperty]
109
string name = "John Wick";
110
111
[ConfigProperty]
112
int _age = 59;
113
}
114
```
115
116
#### 为属性添加注释
117
118
```csharp
119
partial class SystemConfig
120
{
121
/// <summary>
122
/// 名称
123
/// 这段注释会自动添加至自动生成的Name属性上
124
/// </summary>
125
[ConfigProperty]
126
string name;
127
128
[ConfigProperty]
129
int _age;
130
}
131
```
132
133
#### 使用部分方法来设置get和set方法
134
135
```csharp
136
partial class SystemConfig
137
{
138
[ConfigProperty]
139
string name;
140
141
[ConfigProperty]
142
int _age;
143
144
static partial void GetNameProperty()
145
{
146
Console.WriteLine("获取了Name");
147
}
148
149
static partial void SetNameProperty(string value)
150
{
151
Console.WriteLine($"设置了Name:从{Name}变为了{value}");
152
}
153
154
static partial void GetAgeProperty()
155
{
156
Console.WriteLine("获取了Age");
157
}
158
159
static partial void SetAgeProperty(int value)
160
{
161
Console.WriteLine($"设置了Age:从{Age}变为了{value}");
162
}
163
}
164
```
165
166
---
167
168
## 使用LANDeviceDetector来检测本地局域网内的所有设备
169
170
#### 基础用法
171
172
```csharp
173
var lANDeviceDetector = new LANDeviceDetector();
174
lANDeviceDetector.DeviceFind += (sender) =>
175
{
176
Console.WriteLine($"IP地址:{sender.IPAddress}\t设备名称:{sender.DeviceName}");
177
};
178
await lANDeviceDetector.StartDetecting();
179
```
180
181
#### 自定义扫描频段
182
183
```csharp
184
var lANDeviceDetector = new LANDeviceDetector("100.73.121.*");//这将扫描100.73.121.1到100.73.121.255的IP地址
185
lANDeviceDetector.DeviceFind += (sender) =>
186
{
187
Console.WriteLine($"IP地址:{sender.IPAddress}\t设备名称:{sender.DeviceName}");
188
};
189
await lANDeviceDetector.StartDetecting();
190
```
191
192
#### 自定义超时
193
194
```csharp
195
var lANDeviceDetector = new LANDeviceDetector("100.73.121.*", 2000);//这将会设置超时为2000ms
196
lANDeviceDetector.DeviceFind += (sender) =>
197
{
198
Console.WriteLine($"IP地址:{sender.IPAddress}\t设备名称:{sender.DeviceName}");
199
};
200
await lANDeviceDetector.StartDetecting();
201
```
202
203
---
204
205
## 使用X方法分析对象信息,简化调试流程
206
207
#### 在控制台输出(仅适用于C#的控制台应用程序)
208
209
```csharp
210
var testClass = new TestClass("测试名称", "测试描述", 15);//假如这是你需要分析的某个对象
211
testClass.X();//这会将该对象的所有信息输出到控制台
212
```
213
214
#### 在调试信息中输出(使用与所有类型的C#程序)
215
216
```csharp
217
var testClass = new TestClass("测试名称", "测试描述", 15);//假如这是你需要分析的某个对象
218
testClass.XL();//这会将该对象的所有信息输出到调试信息输出中
219
```
220
221
---
222
223
## XFE的ChatGPT使用示例
224
225
#### 最简单的用法
226
227
```csharp
228
//询问GPT并接收回复
229
var result = await XFEChatGPT.SendAndGetGPTResponse("你好");
230
Console.WriteLine(result);
231
```
232
233
#### 一般用法
234
235
```csharp
236
//使用XFEChatGPT类来进行GPT的交互
237
XFEChatGPT xFEChatGPT = new XFEChatGPT("你是一个人工智能AI", true);
238
239
//订阅事件
240
xFEChatGPT.XFEChatGPTMessageReceived += (sender, e) =>
241
{
242
switch (e.GenerateState)
243
{
244
case GenerateState.Start:
245
Console.Write("【输出开始】ChatGPT:");
246
break;
247
case GenerateState.Continue:
248
Console.Write(e.Message);
249
break;
250
case GenerateState.End:
251
Console.WriteLine("【输出完成】");
252
break;
253
case GenerateState.Error:
254
Console.WriteLine($"【发生错误】{e.Message}");
255
break;
256
default:
257
break;
258
}
259
};
260
261
//输入询问内容
262
var askContent = Console.ReadLine();
263
264
//发送生成随机ID并询问内容
265
xFEChatGPT.SendGPTMessage(Guid.NewGuid().ToString(), askContent);
266
```
267
268
#### 推荐用法
269
270
```csharp
271
//创建有记忆功能的XFEChatGPT对象
272
MemorableXFEChatGPT memorableXFEChatGPT = new MemorableXFEChatGPT();
273
274
//创建一个新的对话并设置System内容
275
memorableXFEChatGPT.CreateDialog("新的对话ID", "你是一个由寰宇朽力网络科技开发的人工智能AI", true, true);
276
277
//订阅消息接收事件
278
memorableXFEChatGPT.XFEChatGPTMessageReceived += (sender, e) =>
279
{
280
switch (e.GenerateState)
281
{
282
case GenerateState.Start:
283
Console.Write("【输出开始】ChatGPT:");
284
break;
285
case GenerateState.Continue:
286
Console.Write(e.Message);
287
break;
288
case GenerateState.End:
289
Console.WriteLine("【输出完成】");
290
break;
291
case GenerateState.Error:
292
Console.WriteLine($"【发生错误】{e.Message}");
293
break;
294
default:
295
break;
296
}
297
};
298
299
//读取询问内容
300
var askContent = Console.ReadLine();
301
302
//填写之前创建的对话ID,生成随机的消息ID,并输入刚刚读取的询问内容
303
memorableXFEChatGPT.AskChatGPT("新的对话ID", Guid.NewGuid().ToString(), askContent);
304
```
305
306
---
307
308
## 自动生成实现类
309
310
```csharp
311
[CreateImpl]
312
abstract class TestAbstractClass(int num)
313
{
314
public int Num { get; set; } = num;
315
}
316
317
class Program
318
{
319
static void Main(string[] args)
320
{
321
var testAbstractClass = new TestAbstractClassImpl(123);
322
Console.WriteLine(testAbstractClass.Num);
323
}
324
}
325
```
326
327
---
328
329
## IO流拓展操作示例
330
331
```csharp
332
// 使用XFEExtension来简化文件读取/写入操作
333
"Hello World!".WriteIn("test.txt");
334
string txt = "test.txt".ReadOut();
335
```
336
337
---
338
339
## XEA加密算法示例
340
341
```csharp
342
// 使用XFEExtension来进行加密操作
343
string text = "这是一段将要加密的文本";
344
$"未加密内容:{text}".CW();
345
string password = "这是一个秘钥";
346
string encrypt = text.XEAEncrypt(password);//加密
347
Console.WriteLine("加密内容:" + encrypt);
348
Console.WriteLine("解密内容:" + encrypt.XEADecrypt(password));//解密
349
```
350
351
---
352
353
## 特性操作示例
354
355
```csharp
356
// 使用XFEExtension来简化特性读取操作
357
string str = testObject.GetAttribute<string>();
358
```
359
360
---
361
362
## 使用XUnit测试框架
363
364
```csharp
365
[CTest]
366
class TestClass : XFECode
367
{
368
[MTest]
369
void Test()
370
{
371
Assert(true, "断言内容");
372
}
373
}
374
public class Program : XFECode
375
{
376
public static void Main(string[] args)
377
{
378
Pause();
379
}
380
}
381
```
382
383
---
384
385
## 快速搭建网络通讯服务器
386
387
```csharp
388
public class CustomServer
389
{
390
CyberCommServer CyberCommServer { get; } = new("http://127.0.0.1:19019/");
391
public async Task StartServer()
392
{
393
CyberCommServer.ServerStarted += CyberCommServer_ServerStarted;
394
CyberCommServer.ConnectionClosed += CyberCommServer_ConnectionClosed;
395
CyberCommServer.ClientConnected += CyberCommServer_ClientConnected;
396
CyberCommServer.MessageReceived += CyberCommServer_MessageReceived;
397
await CyberCommServer.StartCyberCommServer();
398
}
399
400
private void CyberCommServer_MessageReceived(object? sender, CyberCommServerEventArgs e)
401
{
402
e.ReplyMessage("服务器已接收消息");
403
Console.WriteLine($"收到客户端[{e.IpAddress}]消息:{e.TextMessage}");//明文传输实例
404
}
405
406
private void CyberCommServer_ClientConnected(object? sender, CyberCommServerEventArgs e)
407
{
408
Console.WriteLine($"新客户端连接:{e.IpAddress}");
409
}
410
411
private void CyberCommServer_ConnectionClosed(object? sender, CyberCommServerEventArgs e)
412
{
413
Console.WriteLine($"客户端[{e.IpAddress}]断开连接");
414
}
415
416
private void CyberCommServer_ServerStarted(object? sender, EventArgs e)
417
{
418
Console.WriteLine("服务器已启动");
419
}
420
}
421
```
422
423
---
424
425
## 快速搭建网络通讯客户端
426
427
```csharp
428
public class CustomClient
429
{
430
CyberCommClient CyberCommClient { get; } = new("http://127.0.0.1:19019/");
431
public async Task StartClient()
432
{
433
CyberCommClient.Connected += CyberCommClient_Connected;
434
CyberCommClient.ConnectionClosed += CyberCommClient_ConnectionClosed;
435
CyberCommClient.MessageReceived += CyberCommClient_MessageReceived;
436
await CyberCommClient.StartCyberCommClient();
437
}
438
439
private void CyberCommClient_MessageReceived(object? sender, CyberCommClientEventArgs e)
440
{
441
Console.WriteLine($"收到消息:{e.TextMessage}");//接收明文消息
442
//此处可以进行消息回复
443
//e.ReplyMessage();
444
}
445
446
private void CyberCommClient_ConnectionClosed(object? sender, EventArgs e)
447
{
448
Console.WriteLine("与服务器断开连接");
449
}
450
451
private void CyberCommClient_Connected(object? sender, EventArgs e)
452
{
453
Console.WriteLine("已连接到服务器");
454
CyberCommClient.SendTextMessage("这是一条测试消息");//以明文消息为示例
455
}
456
}
457
```
458
459
---
460
461
## 使用XCC网络通讯API接口快速搭建聊天室
462
463
```csharp
464
XCCNetWork xCCNetWork = new();//创建XCC网络通讯基础
465
var group = xCCNetWork.CreateGroup("测试群组", "测试人员");//创建网络通讯中的群组,输入群组名,群内名称
466
#region 订阅事件
467
xCCNetWork.Connected += (sender, e) =>
468
{
469
Console.WriteLine($"群组:{e.Group.GroupId}\t连接成功");
470
group.SendTextMessage("测试消息");
471
};
472
xCCNetWork.ConnectionClosed += (sender, e) =>
473
{
474
Console.WriteLine($"群组:{e.Group.GroupId}\t断开连接");
475
};
476
xCCNetWork.TextMessageReceived += (sender, e) =>
477
{
478
Console.WriteLine($"群组:{e.Group.GroupId}\t收到文本消息:{e.TextMessage}");
479
};
480
#endregion
481
await group.StartXCC();//启动该群组的网络通讯
482
```
483
484
---
485
486
## 使用XFE下载器来加速下载文件(支持继续上次下载、多线程加速下载等操作)
487
488
```csharp
489
XFEDownloader xFEDownloader = new()
490
{
491
DownloadUrl = "https://www.nuget.org/api/v2/package/XFE%E5%90%84%E7%B1%BB%E6%8B%93%E5%B1%95.NetCore/1.2.2",
492
SavePath = "XFEExtension.NetCore.nuget",
493
FileSegmentCount = 9 //设置9个线程来加速下载,建议数量不超过15个
494
};
495
xFEDownloader.BufferDownloaded += (sender, e) =>
496
{
497
Console.WriteLine($"进度:{e.DownloadedBufferSize.FileSize()}/{e.TotalBufferSize?.FileSize()}");
498
};
499
await xFEDownloader.Download();
500
```
@@ -0,0 +1,8 @@
1
## Release 1.0.0
2
3
### New Rules
4
5
Rule ID | Category | Severity | Notes
6
--------|----------|----------|-------
7
XFE0002 | XFEExtension.NetCore.AutoConfig.Diagnostics | Error | ProfileExtensionDiagnostics, [Documentation](https://www.xfegzs.com/codespace/diagnostics/XFE0002.html)
8
XFW0001 | XFEExtension.NetCore.AutoConfig.Diagnostics | Warning | ProfileExtensionDiagnostics, [Documentation](https://www.xfegzs.com/codespace/diagnostics/XFW0001.html)
@@ -0,0 +1,9 @@
1
; Unshipped analyzer release
2
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
3
4
### New Rules
5
6
Rule ID | Category | Severity | Notes
7
--------|----------|----------|-------
8
XFE0002 | XFEExtension.NetCore.AutoConfig.Diagnostics | Error | ProfileExtensionDiagnostics, [Documentation](https://www.xfegzs.com/codespace/diagnostics/XFE0002.html)
9
XFW0001 | XFEExtension.NetCore.AutoConfig.Diagnostics | Warning | ProfileExtensionDiagnostics, [Documentation](https://www.xfegzs.com/codespace/diagnostics/XFW0001.html)
@@ -0,0 +1,62 @@
1
using Microsoft.CodeAnalysis;
2
using Microsoft.CodeAnalysis.CodeActions;
3
using Microsoft.CodeAnalysis.CodeFixes;
4
using Microsoft.CodeAnalysis.CSharp;
5
using Microsoft.CodeAnalysis.CSharp.Syntax;
6
using Microsoft.CodeAnalysis.Text;
7
using System.Collections.Immutable;
8
using System.Linq;
9
using System.Threading.Tasks;
10
using XFEExtension.NetCore.AutoConfig.Diagnostics;
11
12
namespace XFEExtension.NetCore.AutoConfig.CodeFix
13
{
14
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AutoConfigCodeFixProvider))]
15
public class AutoConfigCodeFixProvider : CodeFixProvider
16
{
17
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AutoConfigDiagnostics.AddGetNoResultErrorId, AutoConfigDiagnostics.AddSetNoSetResultWarningId);
18
19
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
20
21
public override Task RegisterCodeFixesAsync(CodeFixContext context)
22
{
23
foreach (var diagnostic in context.Diagnostics)
24
{
25
if (diagnostic.Id == AutoConfigDiagnostics.AddGetNoResultErrorId)
26
{
27
context.RegisterCodeFix(CodeAction.Create(title: "添加返回值方法",
28
createChangedDocument: c => AddReturnFuncAsync(context.Document, diagnostic.Location.SourceSpan, c),
29
equivalenceKey: "添加返回值"),
30
diagnostic: diagnostic);
31
}
32
else if (diagnostic.Id == AutoConfigDiagnostics.AddSetNoSetResultWarningId)
33
{
34
context.RegisterCodeFix(CodeAction.Create(title: "添加字段的设置方法",
35
createChangedDocument: c => AddSetFuncAsync(context.Document, diagnostic.Location.SourceSpan, c),
36
equivalenceKey: "添加字段的设置方法"),
37
diagnostic: diagnostic);
38
}
39
}
40
return Task.CompletedTask;
41
}
42
private async Task<Document> AddReturnFuncAsync(Document document, TextSpan sourceSpan, System.Threading.CancellationToken c)
43
{
44
var root = await document.GetSyntaxRootAsync(c);
45
var fieldDeclaration = root.FindToken(sourceSpan.Start).Parent.AncestorsAndSelf().OfType<FieldDeclarationSyntax>().First();
46
var fieldName = fieldDeclaration.Declaration.Variables.First().Identifier.ValueText;
47
var newAttribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("ProfilePropertyAddGet")).AddArgumentListArguments(SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal($"return Current.{fieldName}"))));
48
var newRoot = root.ReplaceNode(fieldDeclaration, fieldDeclaration.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(newAttribute))));
49
return document.WithSyntaxRoot(newRoot);
50
}
51
52
private async Task<Document> AddSetFuncAsync(Document document, TextSpan sourceSpan, System.Threading.CancellationToken c)
53
{
54
var root = await document.GetSyntaxRootAsync(c);
55
var fieldDeclaration = root.FindToken(sourceSpan.Start).Parent.AncestorsAndSelf().OfType<FieldDeclarationSyntax>().First();
56
var fieldName = fieldDeclaration.Declaration.Variables.First().Identifier.ValueText;
57
var newAttribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("ProfilePropertyAddSet")).AddArgumentListArguments(SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal($"Current.{fieldName} = value"))));
58
var newRoot = root.ReplaceNode(fieldDeclaration, fieldDeclaration.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(newAttribute))));
59
return document.WithSyntaxRoot(newRoot);
60
}
61
}
62
}
@@ -0,0 +1,127 @@
1
using Microsoft.CodeAnalysis;
2
using Microsoft.CodeAnalysis.CSharp;
3
using Microsoft.CodeAnalysis.CSharp.Syntax;
4
using Microsoft.CodeAnalysis.Diagnostics;
5
using System.Collections.Immutable;
6
using System.Linq;
7
using System.Text.RegularExpressions;
8
using XFEExtension.NetCore.AutoConfig.Generator;
9
namespace XFEExtension.NetCore.AutoConfig.Diagnostics
10
{
11
[DiagnosticAnalyzer(LanguageNames.CSharp)]
12
public class AutoConfigDiagnostics : DiagnosticAnalyzer
13
{
14
public const string AddGetNoResultErrorId = "XFE0002";
15
public const string AddSetNoSetResultWarningId = "XFW0001";
16
17
public static readonly DiagnosticDescriptor AddGetNoResultError = new DiagnosticDescriptor(AddGetNoResultErrorId,
18
"Get方法没有返回值",
19
"设置了自定义的Get方法但是没有返回值:'{0}'",
20
"XFEExtension.NetCore.AutoConfig.Diagnostics",
21
DiagnosticSeverity.Error,
22
true,
23
"设置了自定义的Get方法但是没有返回值.",
24
"https://www.xfegzs.com/codespace/diagnostics/XFE0002.html");
25
26
public static readonly DiagnosticDescriptor AddSetNoSetResultWarning = new DiagnosticDescriptor(AddSetNoSetResultWarningId,
27
"Set方法没有设置值",
28
"设置了自定义的Set方法但是没有对实际字段进行操作:'{0}'",
29
"XFEExtension.NetCore.AutoConfig.Diagnostics",
30
DiagnosticSeverity.Warning,
31
true,
32
"设置了自定义的Set方法但是没有对实际字段进行操作.",
33
"https://www.xfegzs.com/codespace/diagnostics/XFW0001.html");
34
35
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(AddGetNoResultError, AddSetNoSetResultWarning);
36
37
public override void Initialize(AnalysisContext context)
38
{
39
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
40
context.EnableConcurrentExecution();
41
context.RegisterSyntaxNodeAction(ProfileExtensionAnalyzer, SyntaxKind.Attribute);
42
}
43
public void ProfileExtensionAnalyzer(SyntaxNodeAnalysisContext context)
44
{
45
foreach (var syntaxTree in context.Compilation.SyntaxTrees)
46
{
47
var root = syntaxTree.GetRoot();
48
foreach (var classDeclaration in root.DescendantNodes().OfType<ClassDeclarationSyntax>())
49
{
50
foreach (var fieldDeclaration in ProfilePropertyAutoGenerator.GetFieldDeclarations(classDeclaration))
51
{
52
if (fieldDeclaration.AttributeLists.Any(ProfilePropertyAutoGenerator.IsProfilePropertyAddGetAttribute))
53
{
54
var getAttributeHasResult = false;
55
var attributeSyntaxList = ProfilePropertyAutoGenerator.GetProfilePropertyAddGetAttributeList(fieldDeclaration);
56
foreach (var attributeSyntax in attributeSyntaxList)
57
{
58
if (attributeSyntax.ArgumentList is null)
59
{
60
continue;
61
}
62
var argument = attributeSyntax.ArgumentList.Arguments.First();
63
var funcText = string.Empty;
64
if (argument.Expression is LiteralExpressionSyntax)
65
{
66
funcText = argument.Expression.GetText().ToString();
67
}
68
else if (argument.Expression is InterpolatedStringExpressionSyntax)
69
{
70
funcText = argument.Expression.GetText().ToString();
71
}
72
else if (argument.Expression is InvocationExpressionSyntax)
73
{
74
funcText = argument.Expression.GetText().ToString();
75
}
76
if (funcText.Contains("return"))
77
{
78
getAttributeHasResult = true;
79
}
80
}
81
if (!getAttributeHasResult)
82
{
83
var diagnostic = Diagnostic.Create(AddGetNoResultError, attributeSyntaxList.Last().GetLocation(), fieldDeclaration.Declaration.Variables.First().Identifier.ValueText);
84
context.ReportDiagnostic(diagnostic);
85
}
86
}
87
if (fieldDeclaration.AttributeLists.Any(ProfilePropertyAutoGenerator.IsProfilePropertyAddSetAttribute))
88
{
89
var setAttributeSetResult = false;
90
var attributeSyntaxList = ProfilePropertyAutoGenerator.GetProfilePropertyAddSetAttributeList(fieldDeclaration);
91
foreach (var attributeSyntax in attributeSyntaxList)
92
{
93
if (attributeSyntax.ArgumentList is null)
94
{
95
continue;
96
}
97
var argument = attributeSyntax.ArgumentList.Arguments.First();
98
var funcText = string.Empty;
99
if (argument.Expression is LiteralExpressionSyntax)
100
{
101
funcText = argument.Expression.GetText().ToString();
102
}
103
else if (argument.Expression is InterpolatedStringExpressionSyntax)
104
{
105
funcText = argument.Expression.GetText().ToString();
106
}
107
else if (argument.Expression is InvocationExpressionSyntax)
108
{
109
funcText = argument.Expression.GetText().ToString();
110
}
111
if (Regex.IsMatch(funcText, $@"{fieldDeclaration.Declaration.Variables.First().Identifier.ValueText}\s*=\s*value"))
112
{
113
setAttributeSetResult = true;
114
}
115
}
116
if (!setAttributeSetResult)
117
{
118
var diagnostic = Diagnostic.Create(AddSetNoSetResultWarning, attributeSyntaxList.Last().GetLocation(), fieldDeclaration.Declaration.Variables.First().Identifier.ValueText);
119
context.ReportDiagnostic(diagnostic);
120
}
121
}
122
}
123
}
124
}
125
}
126
}
127
}
@@ -0,0 +1,293 @@
1
using Microsoft.CodeAnalysis;
2
using Microsoft.CodeAnalysis.CSharp;
3
using Microsoft.CodeAnalysis.CSharp.Syntax;
4
using System.Collections.Generic;
5
using System.Linq;
6
7
namespace XFEExtension.NetCore.AutoConfig.Generator
8
{
9
[Generator]
10
public class ProfilePropertyAutoGenerator : ISourceGenerator
11
{
12
public void Initialize(GeneratorInitializationContext context)
13
{
14
context.RegisterForSyntaxNotifications(() => new ProfilePropertySyntaxReceiver());
15
}
16
17
public void Execute(GeneratorExecutionContext context)
18
{
19
var syntaxTrees = context.Compilation.SyntaxTrees;
20
foreach (var syntaxTree in syntaxTrees)
21
{
22
var root = syntaxTree.GetRoot();
23
var classDeclarations = GetClassDeclarations(root);
24
var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
25
var fileScopedNamespaceDeclarationSyntax = GetFileScopedNamespaceDeclaration(root);
26
foreach (var classDeclaration in classDeclarations)
27
{
28
var fieldDeclarationSyntaxes = GetFieldDeclarations(classDeclaration);
29
if (fieldDeclarationSyntaxes is null || !fieldDeclarationSyntaxes.Any())
30
{
31
continue;
32
}
33
var className = classDeclaration.Identifier.ValueText;
34
var attributeSyntax = SyntaxFactory.AttributeList(
35
SyntaxFactory.SingletonSeparatedList(
36
SyntaxFactory.Attribute(SyntaxFactory.ParseName("global::XFEExtension.NetCore.ProfileExtension.ProfileFieldAutoGenerateAttribute"))));
37
var properties = new List<PropertyDeclarationSyntax>();
38
var methods = new List<MethodDeclarationSyntax>();
39
foreach (var fieldDeclarationSyntax in fieldDeclarationSyntaxes)
40
{
41
var variableDeclaration = fieldDeclarationSyntax.Declaration.Variables.First();
42
var fieldName = variableDeclaration.Identifier.Text;
43
var propertyName = fieldName[0] == '_' ? fieldName[1].ToString().ToUpper() + fieldName.Substring(2) : fieldName[0].ToString().ToUpper() + fieldName.Substring(1);
44
var getMethodName = $"Get{propertyName}Property";
45
var setMethodName = $"Set{propertyName}Property";
46
GetProfilePropertyAttributeList(fieldDeclarationSyntax).ForEach(attribute =>
47
{
48
if (attribute.ArgumentList is null)
49
{
50
return;
51
}
52
var argument = attribute.ArgumentList.Arguments.First();
53
if (argument.Expression is LiteralExpressionSyntax literalExpressionSyntax)
54
{
55
propertyName = literalExpressionSyntax.Token.ValueText;
56
}
57
});
58
var propertyType = fieldDeclarationSyntax.Declaration.Type;
59
#region Trivia头
60
var triviaText = $@"/// <inheritdoc cref=""{fieldName}""/>
61
/// <remarks>
62
/// <seealso cref=""{propertyName}""/> 是根据 <seealso cref=""{fieldName}""/> 自动生成的属性<br/><br/>
63
/// <code><seealso langword=""get""/>方法已生成以下代码: ○ <seealso cref=""{className}.{getMethodName}()""/>;<br/>";
64
#endregion
65
var getExpressionStatements = new List<StatementSyntax>()
66
{
67
SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"{getMethodName}()")).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
68
};
69
if (fieldDeclarationSyntax.AttributeLists.Any(IsProfilePropertyAddGetAttribute))
70
{
71
GetProfilePropertyAddGetAttributeList(fieldDeclarationSyntax).ForEach(attribute =>
72
{
73
if (attribute.ArgumentList is null)
74
{
75
return;
76
}
77
var argument = attribute.ArgumentList.Arguments.First();
78
var funcText = string.Empty;
79
if (argument.Expression is LiteralExpressionSyntax literalExpressionSyntax)
80
funcText = literalExpressionSyntax.Token.ValueText;
81
if (argument.Expression is InterpolatedStringExpressionSyntax interpolatedStringExpressionSyntax)
82
funcText = interpolatedStringExpressionSyntax.Contents.ToString();
83
if (argument.Expression is InvocationExpressionSyntax invocationExpressionSyntax)
84
funcText = invocationExpressionSyntax.GetText().ToString();
85
getExpressionStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(funcText)).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)));
86
#region Get方法注释
87
triviaText += $"\n///\t\t\t\t○ {funcText.Replace("\n", "<br/>").Replace("return", "<seealso langword=\"return\"/>").Replace(fieldName, $"<seealso langword=\"{fieldName}\"/>")};<br/>";
88
#endregion
89
});
90
}
91
else
92
{
93
getExpressionStatements.Add(SyntaxFactory.ReturnStatement(SyntaxFactory.ParseExpression($"Current.{fieldName}")));
94
#region Get方默认注释
95
triviaText += $@"
96
/// ○ <seealso langword=""return""/> <seealso langword=""{fieldName}""/>;";
97
#endregion
98
}
99
#region Get方法尾及Set方法头注释
100
triviaText += $@"
101
/// </code>
102
/// <br/>
103
/// <code><seealso langword=""set""/>方法已生成以下代码: ○ <seealso cref=""{className}.{setMethodName}({propertyType})""/>;<br/>";
104
#endregion
105
var setExpressionStatements = new List<StatementSyntax>()
106
{
107
SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"{setMethodName}(value)")).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
108
};
109
if (fieldDeclarationSyntax.AttributeLists.Any(IsProfilePropertyAddSetAttribute))
110
{
111
GetProfilePropertyAddSetAttributeList(fieldDeclarationSyntax).ForEach(attribute =>
112
{
113
if (attribute.ArgumentList is null)
114
{
115
return;
116
}
117
var argument = attribute.ArgumentList.Arguments.First();
118
var funcText = string.Empty;
119
if (argument.Expression is LiteralExpressionSyntax literalExpressionSyntax)
120
funcText = literalExpressionSyntax.Token.ValueText;
121
else if (argument.Expression is InterpolatedStringExpressionSyntax interpolatedStringExpressionSyntax)
122
funcText = interpolatedStringExpressionSyntax.Contents.ToString();
123
else if (argument.Expression is InvocationExpressionSyntax invocationExpressionSyntax)
124
funcText = invocationExpressionSyntax.GetText().ToString();
125
setExpressionStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(funcText)).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)));
126
#region Set方法注释
127
triviaText += $"\n///\t\t\t\t○ {funcText.Replace("\n", "<br/>").Replace(fieldName, $"<seealso langword=\"{fieldName}\"/>").Replace("value", "<seealso langword=\"value\"/>")};<br/>";
128
#endregion
129
});
130
}
131
else
132
{
133
setExpressionStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"Current.{fieldName} = value")));
134
#region Set方法默认注释
135
triviaText += $@"
136
/// ○ <seealso langword=""{fieldName}""/> = <seealso langword=""value""/>;<br/>";
137
#endregion
138
}
139
setExpressionStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"global::XFEExtension.NetCore.ProfileExtension.XFEProfile.SaveProfile(typeof({className}))")));
140
#region Set方法中的保存方法的注释
141
triviaText += $@"
142
/// ○ <seealso cref=""global::XFEExtension.NetCore.ProfileExtension.XFEProfile.SaveProfile(ProfileInfo)""/>";
143
#endregion
144
#region Trivia尾
145
triviaText += @"
146
/// </code>
147
/// </remarks>
148
";
149
#endregion
150
var property = SyntaxFactory.PropertyDeclaration(propertyType, propertyName)
151
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)))
152
.AddAttributeLists(attributeSyntax)
153
.WithAccessorList(SyntaxFactory.AccessorList(
154
SyntaxFactory.List(new[]
155
{
156
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
157
.WithBody(SyntaxFactory.Block(getExpressionStatements)),
158
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
159
.WithBody(SyntaxFactory.Block(setExpressionStatements))
160
})))
161
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia(triviaText));
162
var getMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("void"), getMethodName)
163
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.PartialKeyword)))
164
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
165
var setMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("void"), setMethodName)
166
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.PartialKeyword)))
167
.AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("value")).WithType(propertyType))
168
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
169
methods.Add(getMethod);
170
methods.Add(setMethod);
171
properties.Add(property.NormalizeWhitespace());
172
}
173
var profileClassSyntaxTree = GenerateProfileClassSyntaxTree(classDeclaration, usingDirectives, properties, methods, fileScopedNamespaceDeclarationSyntax);
174
context.AddSource($"{className}.g.cs", profileClassSyntaxTree.ToString());
175
}
176
}
177
}
178
179
public static bool IsProfilePropertyAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "ProfileProperty");
180
181
public static List<AttributeSyntax> GetProfilePropertyAttributeList(FieldDeclarationSyntax fieldDeclaration) => fieldDeclaration.AttributeLists.Where(IsProfilePropertyAttribute).SelectMany(attributeList => attributeList.Attributes).ToList();
182
183
public static bool IsAutoLoadProfileAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "AutoLoadProfile");
184
185
public static List<AttributeSyntax> GetAutoLoadProfileAttribute(FieldDeclarationSyntax fieldDeclaration) => fieldDeclaration.AttributeLists.Where(IsAutoLoadProfileAttribute).SelectMany(attributeList => attributeList.Attributes).ToList();
186
187
public static bool IsProfilePropertyAddGetAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "ProfilePropertyAddGet");
188
189
public static List<AttributeSyntax> GetProfilePropertyAddGetAttributeList(FieldDeclarationSyntax fieldDeclaration) => fieldDeclaration.AttributeLists.Where(IsProfilePropertyAddGetAttribute).SelectMany(attributeList => attributeList.Attributes).ToList();
190
191
public static bool IsProfilePropertyAddSetAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "ProfilePropertyAddSet");
192
193
public static List<AttributeSyntax> GetProfilePropertyAddSetAttributeList(FieldDeclarationSyntax fieldDeclaration) => fieldDeclaration.AttributeLists.Where(IsProfilePropertyAddSetAttribute).SelectMany(attributeList => attributeList.Attributes).ToList();
194
195
public static FileScopedNamespaceDeclarationSyntax GetFileScopedNamespaceDeclaration(SyntaxNode rootNode)
196
{
197
var namespaceResults = rootNode.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
198
if (namespaceResults != null && namespaceResults.Count() > 0)
199
return namespaceResults.First();
200
return null;
201
}
202
203
public static IEnumerable<FieldDeclarationSyntax> GetFieldDeclarations(ClassDeclarationSyntax classDeclaration) => classDeclaration.DescendantNodes()
204
.OfType<FieldDeclarationSyntax>()
205
.Where(fieldDeclarationSyntax => fieldDeclarationSyntax.AttributeLists.Any(IsProfilePropertyAttribute) && !fieldDeclarationSyntax.Modifiers.Any(SyntaxKind.StaticKeyword));
206
207
public static IEnumerable<ClassDeclarationSyntax> GetClassDeclarations(SyntaxNode rootNode) => rootNode.DescendantNodes()
208
.OfType<ClassDeclarationSyntax>()
209
.Where(classDeclaration => classDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword) && !classDeclaration.Modifiers.Any(SyntaxKind.StaticKeyword));
210
211
private static SyntaxTree GenerateProfileClassSyntaxTree(ClassDeclarationSyntax classDeclaration, UsingDirectiveSyntax[] usingDirectiveSyntaxes, List<PropertyDeclarationSyntax> propertyDeclarationSyntaxes, List<MethodDeclarationSyntax> methodDeclarationSyntaxes, FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax)
212
{
213
var className = classDeclaration.Identifier.ValueText;
214
var triviaText = $@"/// <remarks>
215
/// <code><seealso cref=""{className}""/> 已自动实现以下属性:</code><br/>
216
/// <code>
217
";
218
triviaText += string.Join("<br/>\n", propertyDeclarationSyntaxes.Select(propertyDeclarationSyntax => $"/// ○ <seealso cref=\"{propertyDeclarationSyntax.Identifier}\"/>")) + "\n/// </code><br/>\n/// <code>来自<seealso cref=\"global::XFEExtension.NetCore.ProfileExtension.XFEProfile\"/></code>\n/// </remarks>\n";
219
var memberDeclarations = new List<MemberDeclarationSyntax>
220
{
221
SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName(className), "Current")
222
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)))
223
.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Attribute(SyntaxFactory.ParseName("global::XFEExtension.NetCore.ProfileExtension.ProfileInstanceAttribute")))))
224
.WithAccessorList(SyntaxFactory.AccessorList(SyntaxFactory.List(
225
new[]
226
{
227
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
228
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
229
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
230
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
231
})))
232
.WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression($"new {className}()")))
233
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
234
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
235
/// 该配置文件的实例<br/>
236
/// <seealso cref=""Current""/> 是 <seealso cref=""{className}""/> 配置文件类的实例数据
237
/// </summary>
238
"))
239
};
240
memberDeclarations.AddRange(propertyDeclarationSyntaxes);
241
memberDeclarations.AddRange(methodDeclarationSyntaxes);
242
var staticConstructorSyntax = SyntaxFactory.ConstructorDeclaration(className)
243
.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword))
244
.WithBody(SyntaxFactory.Block(
245
SyntaxFactory.ParseStatement($"global::XFEExtension.NetCore.ProfileExtension.XFEProfile.LoadProfiles(typeof({className}));")));
246
if (classDeclaration.AttributeLists.Any(IsAutoLoadProfileAttribute))
247
{
248
var autoLoadProfileAttribute = classDeclaration.AttributeLists.First(attributeList => IsAutoLoadProfileAttribute(attributeList)).Attributes.First();
249
if (autoLoadProfileAttribute.ArgumentList != null)
250
{
251
var argument = autoLoadProfileAttribute.ArgumentList.Arguments.First();
252
if (argument.Expression is LiteralExpressionSyntax literalExpressionSyntax && literalExpressionSyntax.Token.ValueText == "true")
253
{
254
memberDeclarations.Add(staticConstructorSyntax);
255
}
256
}
257
else
258
{
259
memberDeclarations.Add(staticConstructorSyntax);
260
}
261
}
262
else
263
{
264
memberDeclarations.Add(staticConstructorSyntax);
265
}
266
var profileClass = SyntaxFactory.ClassDeclaration(className)
267
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PartialKeyword))
268
.AddMembers(memberDeclarations.ToArray())
269
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia(triviaText))
270
.NormalizeWhitespace();
271
MemberDeclarationSyntax memberDeclaration;
272
if (fileScopedNamespaceDeclarationSyntax is null)
273
{
274
var namespaceDeclaration = classDeclaration.FirstAncestorOrSelf<NamespaceDeclarationSyntax>();
275
if (namespaceDeclaration is null)
276
memberDeclaration = profileClass;
277
else
278
memberDeclaration = SyntaxFactory.NamespaceDeclaration(namespaceDeclaration.Name)
279
.AddMembers(profileClass);
280
}
281
else
282
{
283
memberDeclaration = SyntaxFactory.FileScopedNamespaceDeclaration(fileScopedNamespaceDeclarationSyntax.Name)
284
.AddMembers(profileClass);
285
}
286
var profileClassCompilationUnit = SyntaxFactory.CompilationUnit()
287
.AddUsings(usingDirectiveSyntaxes)
288
.AddMembers(memberDeclaration)
289
.NormalizeWhitespace();
290
return SyntaxFactory.SyntaxTree(profileClassCompilationUnit);
291
}
292
}
293
}
二进制文件已变更,无法进行逐行预览。