XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEExtension.NetCore.AutoConfig

【DLL】自动实现配置文件的存储

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFEExtension.NetCore.AutoConfig

从XFEExtension.NetCore中迁移

f8dfe13
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

20 个文件 +1528 -3
Added README.md +500 -0
@@ -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 ```
Added XFEExtension.NetCore.AutoConfig.Analyzer/AnalyzerReleases.Shipped.md +8 -0
@@ -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)
Added XFEExtension.NetCore.AutoConfig.Analyzer/AnalyzerReleases.Unshipped.md +9 -0
@@ -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)
Added XFEExtension.NetCore.AutoConfig.Analyzer/CodeFix/AutoConfigCodeFixProvider.cs +62 -0
@@ -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 }
Added XFEExtension.NetCore.AutoConfig.Analyzer/Diagnostics/AutoConfigDiagnostics.cs +127 -0
@@ -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 }
Added XFEExtension.NetCore.AutoConfig.Analyzer/Generator/ProfilePropertyAutoGenerator.cs +293 -0
@@ -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 }
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
二进制文件已变更,无法进行逐行预览。