Skip to content

Commit 2bfe8bf

Browse files
authored
Merge pull request #62 from dotnet-campus/t/walterlv/delegate
减少委托,优化分配(竟然顺便也优化了解析耗时)
2 parents a1e0e42 + 7756384 commit 2bfe8bf

File tree

17 files changed

+371
-271
lines changed

17 files changed

+371
-271
lines changed

samples/DotNetCampus.CommandLine.Sample/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ static void Main(string[] args)
7777
stopwatch.Restart();
7878
for (var i = 0; i < testCount; i++)
7979
{
80-
_ = newCommandLine.As<Options>(OptionsBuilder.CreateInstance);
80+
var context = new CommandRunningContext { CommandLine = newCommandLine };
81+
_ = new OptionsBuilder().Build(context);
8182
}
8283
stopwatch.Stop();
8384
Console.Write($"{stopwatch.ElapsedMilliseconds.ToString(),7} ms | ");

src/DotNetCampus.CommandLine.Analyzer/Generators/InterceptorGenerator.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ private void GenerateCommandLineAddHandlerCode(TypeDeclarationSourceTextBuilder
151151
.AddTypeConstraints("where T : class, global::DotNetCampus.Cli.ICommandHandler")
152152
.AddRawStatement(GenerateComment(model))
153153
.AddRawStatements($"""
154-
return commandLine.AsRunner().AddHandler<T>(global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CreateInstance);
154+
return commandLine.AsRunner().AddHandler<T>(global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, new global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.Metadata());
155155
"""));
156156
}
157157

@@ -166,7 +166,7 @@ private void GenerateCommandLineAddHandlerActionCode(TypeDeclarationSourceTextBu
166166
.AddTypeConstraints("where T : class")
167167
.AddRawStatement(GenerateComment(model))
168168
.AddRawStatements($"""
169-
return commandLine.AsRunner().AddHandler<T>(handler, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CreateInstance);
169+
return commandLine.AsRunner().AddHandler<T>(handler, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, new global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.Metadata());
170170
"""));
171171
}
172172

@@ -293,7 +293,7 @@ private void GenerateCommandBuilderAddHandlerCode(TypeDeclarationSourceTextBuild
293293
.AddTypeConstraints("where T : class, global::DotNetCampus.Cli.ICommandHandler")
294294
.AddRawStatement(GenerateComment(model))
295295
.AddRawStatements($"""
296-
return global::DotNetCampus.Cli.CommandRunnerBuilderExtensions.AddHandler<T>(builder, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CreateInstance);
296+
return global::DotNetCampus.Cli.CommandRunnerBuilderExtensions.AddHandler<T>(builder, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, new global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.Metadata());
297297
"""));
298298
}
299299

@@ -310,7 +310,7 @@ private void GenerateStatedCommandBuilderAddHandlerCode(TypeDeclarationSourceTex
310310
.AddTypeConstraints("where T : class, global::DotNetCampus.Cli.ICommandHandler<TState>")
311311
.AddRawStatement(GenerateComment(model))
312312
.AddRawStatements($"""
313-
return builder.AddHandler<T>(global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CreateInstance);
313+
return builder.AddHandler<T>(global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, new global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.Metadata());
314314
"""));
315315
}
316316

@@ -327,7 +327,7 @@ private void GenerateCommandBuilderAddHandlerActionCode(TypeDeclarationSourceTex
327327
.AddTypeConstraints("where T : class")
328328
.AddRawStatement(GenerateComment(model))
329329
.AddRawStatements($"""
330-
return global::DotNetCampus.Cli.CommandRunnerBuilderExtensions.AddHandler<T>(builder, handler, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CreateInstance);
330+
return global::DotNetCampus.Cli.CommandRunnerBuilderExtensions.AddHandler<T>(builder, handler, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, new global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.Metadata());
331331
"""));
332332
}
333333

src/DotNetCampus.CommandLine.Analyzer/Generators/ModelBuilderGenerator.cs

Lines changed: 32 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System.Text;
21
using DotNetCampus.CommandLine.CodeAnalysis;
32
using DotNetCampus.CommandLine.Generators.Builders;
43
using DotNetCampus.CommandLine.Generators.ModelProviding;
@@ -29,15 +28,6 @@ private void Execute(SourceProductionContext context, CommandObjectGeneratingMod
2928

3029
var code = GenerateCommandObjectCreatorCode(model);
3130
context.AddSource($"CommandLine.Models/{model.CommandObjectType.ToDisplayString()}.cs", code);
32-
33-
// if (model.UseFullStackParser)
34-
// {
35-
// var originalCode = EmbeddedSourceFiles.Enumerate(null)
36-
// .First(x => x.FileName == "CommandLineParser.cs")
37-
// .Content;
38-
// var parserCode = GenerateParserCode(originalCode, model);
39-
// context.AddSource($"CommandLine.Models/{model.Namespace}.{model.CommandObjectType.Name}.parser.cs", parserCode);
40-
// }
4131
}
4232

4333
private static bool ReportDiagnostics(SourceProductionContext context, CommandObjectGeneratingModel model)
@@ -74,24 +64,21 @@ private string GenerateCommandObjectCreatorCode(CommandObjectGeneratingModel mod
7464
.AddTypeDeclaration(GenerateBuilderTypeDeclarationLine(model), t => t
7565
.WithSummaryComment($"""辅助 <see cref="{model.CommandObjectType.ToUsingString()}"/> 生成命令行选项、子命令或处理函数的创建。""")
7666
.AddRawMembers(GenerateCommandNames(model))
77-
.AddMethodDeclaration(
78-
$"public static {model.CommandObjectType.ToUsingString()} CreateInstance(global::DotNetCampus.Cli.Compiler.CommandRunningContext context)",
79-
m => m
80-
.AddRawStatements($"return new {model.Namespace}.{model.GetBuilderTypeName()}().Build(context);"))
67+
.AddTypeDeclaration(GenerateMetadataTypeDeclarationLine(model), nt => GenerateCommandObjectMetadata(nt, model))
8168
.AddRawMembers(model.OptionProperties.Select(GenerateArgumentPropertyCode))
8269
.AddRawMembers(model.EnumeratePositionalArgumentExcludingSameNameOptions().Select(GenerateArgumentPropertyCode))
8370
.AddRawText(GenerateBuildCode(model))
8471
.AddMethodDeclaration(
85-
"private global::DotNetCampus.Cli.Utils.Parsers.OptionValueMatch MatchLongOption(ReadOnlySpan<char> longOption, bool defaultCaseSensitive, global::DotNetCampus.Cli.CommandNamingPolicy namingPolicy)",
72+
"public global::DotNetCampus.Cli.Utils.Parsers.OptionValueMatch MatchLongOption(ReadOnlySpan<char> longOption, bool defaultCaseSensitive, global::DotNetCampus.Cli.CommandNamingPolicy namingPolicy)",
8673
m => GenerateMatchLongOptionCode(m, model))
8774
.AddMethodDeclaration(
88-
"private global::DotNetCampus.Cli.Utils.Parsers.OptionValueMatch MatchShortOption(ReadOnlySpan<char> shortOption, bool defaultCaseSensitive)",
75+
"public global::DotNetCampus.Cli.Utils.Parsers.OptionValueMatch MatchShortOption(ReadOnlySpan<char> shortOption, bool defaultCaseSensitive)",
8976
m => GenerateMatchShortOptionCode(m, model))
9077
.AddMethodDeclaration(
91-
"private global::DotNetCampus.Cli.Utils.Parsers.PositionalArgumentValueMatch MatchPositionalArguments(ReadOnlySpan<char> value, int argumentIndex)",
78+
"public global::DotNetCampus.Cli.Utils.Parsers.PositionalArgumentValueMatch MatchPositionalArguments(ReadOnlySpan<char> value, int argumentIndex)",
9279
m => GenerateMatchPositionalArgumentsCode(m, model))
9380
.AddMethodDeclaration(
94-
"private void AssignPropertyValue(string propertyName, int propertyIndex, ReadOnlySpan<char> key, ReadOnlySpan<char> value)",
81+
"public void AssignPropertyValue(string propertyName, int propertyIndex, ReadOnlySpan<char> key, ReadOnlySpan<char> value)",
9582
m => m
9683
.Condition(model.OptionProperties.Count > 0 || model.PositionalArgumentProperties.Count > 0, b => b
9784
.AddBracketScope("switch (propertyIndex)", l => l
@@ -114,8 +101,14 @@ private static string GenerateBuilderTypeDeclarationLine(CommandObjectGenerating
114101
{
115102
var modifier = model.IsPublic ? "public" : "internal";
116103
return model.UseFullStackParser
117-
? $"{modifier} partial struct {model.GetBuilderTypeName()}()"
118-
: $"{modifier} sealed class {model.GetBuilderTypeName()}";
104+
? $"{modifier} partial struct {model.GetBuilderTypeName()}() : global::DotNetCampus.Cli.Compiler.ICommandObjectBuilder // 临时继承,后面要去掉"
105+
: $"{modifier} sealed class {model.GetBuilderTypeName()} : global::DotNetCampus.Cli.Compiler.ICommandObjectBuilder";
106+
}
107+
108+
private static string GenerateMetadataTypeDeclarationLine(CommandObjectGeneratingModel model)
109+
{
110+
var modifier = model.IsPublic ? "public" : "internal";
111+
return $"{modifier} sealed class Metadata : global::DotNetCampus.Cli.Compiler.ICommandObjectMetadata";
119112
}
120113

121114
private static string GenerateCommandNames(CommandObjectGeneratingModel model)
@@ -130,6 +123,13 @@ private static string GenerateCommandNames(CommandObjectGeneratingModel model)
130123
""";
131124
}
132125

126+
private void GenerateCommandObjectMetadata(TypeDeclarationSourceTextBuilder builder, CommandObjectGeneratingModel model)
127+
{
128+
builder
129+
.AddMethodDeclaration("public object Build(global::DotNetCampus.Cli.Compiler.CommandRunningContext context)", m => m
130+
.AddRawStatement($"return new {model.Namespace}.{model.GetBuilderTypeName()}().Build(context);"));
131+
}
132+
133133
private string GenerateArgumentPropertyCode(PropertyGeneratingModel model) =>
134134
$"private {GetArgumentPropertyTypeName(model)} {model.PropertyName} = new();";
135135

@@ -152,22 +152,16 @@ private static string GenerateBuildCode(CommandObjectGeneratingModel model) => $
152152
return BuildDefault(context.CommandLine);
153153
}
154154
155-
var parser = new global::DotNetCampus.Cli.Utils.Parsers.CommandLineParser(context.CommandLine, "{{model.CommandObjectType.Name}}", {{model.GetCommandLevel()}})
156-
{
157-
MatchLongOption = MatchLongOption,
158-
MatchShortOption = MatchShortOption,
159-
MatchPositionalArguments = MatchPositionalArguments,
160-
AssignPropertyValue = AssignPropertyValue,
161-
};
155+
var parser = new global::DotNetCampus.Cli.Utils.Parsers.CommandLineParser(context.CommandLine, this, "{{model.CommandObjectType.Name}}", {{model.GetCommandLevel()}});
162156
parser.Parse().WithFallback(context);
163157
return BuildCore(context.CommandLine);
164158
}
165159
""";
166160

167-
private MethodDeclarationSourceTextBuilder GenerateMatchLongOptionCode(MethodDeclarationSourceTextBuilder builder, CommandObjectGeneratingModel model)
161+
private void GenerateMatchLongOptionCode(MethodDeclarationSourceTextBuilder builder, CommandObjectGeneratingModel model)
168162
{
169163
var optionProperties = model.OptionProperties;
170-
return builder
164+
builder
171165
.Condition(optionProperties.Count is 0, b => b
172166
.AddRawStatement("// 没有长名称选项,无需匹配。"))
173167
.Otherwise(b => b
@@ -208,11 +202,11 @@ static string GenerateLongOptionEqualsCode(OptionalArgumentPropertyGeneratingMod
208202
}
209203
}
210204

211-
private MethodDeclarationSourceTextBuilder GenerateMatchShortOptionCode(MethodDeclarationSourceTextBuilder builder, CommandObjectGeneratingModel model)
205+
private void GenerateMatchShortOptionCode(MethodDeclarationSourceTextBuilder builder, CommandObjectGeneratingModel model)
212206
{
213207
var optionProperties = model.OptionProperties;
214208
var hasShortName = optionProperties.SelectMany(x => x.GetShortNames()).Any();
215-
return builder
209+
builder
216210
.Condition(!hasShortName, b => b
217211
.AddRawStatement("// 没有短名称选项,无需匹配。"))
218212
.Otherwise(b => b
@@ -259,12 +253,12 @@ static string GenerateOptionEqualsCode(OptionalArgumentPropertyGeneratingModel m
259253
}
260254
}
261255

262-
private MethodDeclarationSourceTextBuilder GenerateMatchPositionalArgumentsCode(MethodDeclarationSourceTextBuilder builder,
256+
private void GenerateMatchPositionalArgumentsCode(MethodDeclarationSourceTextBuilder builder,
263257
CommandObjectGeneratingModel model)
264258
{
265259
var positionalArgumentProperties = model.PositionalArgumentProperties;
266260
var matchAllProperty = positionalArgumentProperties.FirstOrDefault(x => x.Index is 0 && x.Length is int.MaxValue);
267-
return builder
261+
builder
268262
.Condition(positionalArgumentProperties.Count is 0, b => b
269263
.AddRawStatement("// 没有位置参数,无需匹配。")
270264
.AddRawStatement("return global::DotNetCampus.Cli.Utils.Parsers.PositionalArgumentValueMatch.NotMatch;"))
@@ -328,7 +322,7 @@ private string GenerateAssignPropertyValueCode(PropertyGeneratingModel model)
328322
""";
329323
}
330324

331-
private MethodDeclarationSourceTextBuilder GenerateBuildCoreCode(MethodDeclarationSourceTextBuilder builder, CommandObjectGeneratingModel model)
325+
private void GenerateBuildCoreCode(MethodDeclarationSourceTextBuilder builder, CommandObjectGeneratingModel model)
332326
{
333327
var initRawArgumentsProperties = model.RawArgumentsProperties.Where(x => x.IsRequiredOrInit).ToList();
334328
var initOptionProperties = model.OptionProperties.Where(x => x.IsRequiredOrInit).ToList();
@@ -337,7 +331,7 @@ private MethodDeclarationSourceTextBuilder GenerateBuildCoreCode(MethodDeclarati
337331
var setOptionProperties = model.OptionProperties.Where(x => !x.IsRequiredOrInit).ToList();
338332
var setPositionalArgumentProperties = model.PositionalArgumentProperties.Where(x => !x.IsRequiredOrInit).ToList();
339333

340-
return builder
334+
builder
341335
.AddBracketScope($"var result = new {model.CommandObjectType.ToUsingString()}", "{", "};", c => c
342336

343337
// 1. [RawArguments]
@@ -398,7 +392,7 @@ private MethodDeclarationSourceTextBuilder GenerateBuildCoreCode(MethodDeclarati
398392
.AddRawStatement("return result;");
399393
}
400394

401-
private MethodDeclarationSourceTextBuilder GenerateBuildDefaultCode(MethodDeclarationSourceTextBuilder builder, CommandObjectGeneratingModel model)
395+
private void GenerateBuildDefaultCode(MethodDeclarationSourceTextBuilder builder, CommandObjectGeneratingModel model)
402396
{
403397
var initRawArgumentsProperties = model.RawArgumentsProperties.Where(x => x.IsRequiredOrInit).ToList();
404398
var initOptionProperties = model.OptionProperties.Where(x => x.IsRequiredOrInit).ToList();
@@ -411,10 +405,10 @@ private MethodDeclarationSourceTextBuilder GenerateBuildDefaultCode(MethodDeclar
411405
builder.AddRawStatement("""
412406
throw new global::DotNetCampus.Cli.Exceptions.RequiredPropertyNotAssignedException($"The command line arguments doesn't contain any required option or positional argument. Command line: {commandLine}", null!);
413407
""");
414-
return builder;
408+
return;
415409
}
416410

417-
return builder
411+
builder
418412
.AddBracketScope($"var result = new {model.CommandObjectType.ToUsingString()}", "{", "};", c => c
419413

420414
// 1. [RawArguments]
@@ -570,14 +564,6 @@ private string GenerateEnumDeclarationCode(ITypeSymbol enumType)
570564
}
571565
""";
572566
}
573-
574-
private string GenerateParserCode(string originalCode, CommandObjectGeneratingModel model) => new StringBuilder()
575-
.AppendLine("#nullable enable")
576-
.AppendLine("using DotNetCampus.Cli;")
577-
.Append(originalCode)
578-
.Replace("namespace DotNetCampus.Cli.Utils.Parsers;", $"namespace {model.Namespace};")
579-
.Replace("public readonly ref struct CommandLineParser", $"partial struct {model.GetBuilderTypeName()}")
580-
.ToString();
581567
}
582568

583569
file static class Extensions

src/DotNetCampus.CommandLine.Analyzer/Generators/Models/CommandObjectGeneratingModel.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ internal record CommandObjectGeneratingModel
2828

2929
public required IReadOnlyList<PositionalArgumentPropertyGeneratingModel> PositionalArgumentProperties { get; init; }
3030

31-
public string GetBuilderTypeName() => GetBuilderTypeName(CommandObjectType);
31+
public string GetBuilderTypeName()
32+
{
33+
return GetBuilderTypeName(CommandObjectType);
34+
}
3235

3336
public static string GetBuilderTypeName(INamedTypeSymbol commandObjectType)
3437
{

src/DotNetCampus.CommandLine/CommandLine.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,6 @@ public static CommandLine Parse(string singleLineCommandLineArgs, CommandLinePar
102102
public T As<T>() where T : notnull => throw MethodShouldBeInspected();
103103
#pragma warning restore CA1822
104104

105-
/// <summary>
106-
/// 尝试将命令行参数转换为指定类型的实例。
107-
/// </summary>
108-
/// <param name="factory">由拦截器传入的命令处理器创建方法。</param>
109-
/// <typeparam name="T">要转换的类型。</typeparam>
110-
/// <returns>转换后的实例。</returns>
111-
[Pure, EditorBrowsable(EditorBrowsableState.Never)]
112-
public T As<T>(CommandObjectFactory factory) where T : notnull
113-
{
114-
return (T)factory(new CommandRunningContext { CommandLine = this });
115-
}
116-
117105
/// <summary>
118106
/// 输出传入的命令行参数字符串。如果命令行参数中传入的是 URL,此方法会将 URL 转换为普通的命令行参数再输出。
119107
/// </summary>

src/DotNetCampus.CommandLine/CommandLineExceptionHandler.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using DotNetCampus.Cli.Compiler;
12
using DotNetCampus.Cli.Utils.Parsers;
23

34
namespace DotNetCampus.Cli;
@@ -20,6 +21,19 @@ public Task<int> RunAsync()
2021
}
2122
}
2223

24+
internal sealed class CommandLineExceptionHandlerMetadata(bool ignoreAllExceptions) : ICommandObjectMetadata
25+
{
26+
public object Build(CommandRunningContext context)
27+
{
28+
return new CommandLineExceptionHandler(context.CommandLine, ignoreAllExceptions);
29+
}
30+
31+
public Task<int> RunAsync(object createdCommandObject)
32+
{
33+
return ((CommandLineExceptionHandler)createdCommandObject).RunAsync();
34+
}
35+
}
36+
2337
/// <summary>
2438
/// 辅助创建命令行异常处理器。
2539
/// </summary>
@@ -34,6 +48,6 @@ public static class CommandLineExceptionHandlerExtensions
3448
[Obsolete("此方法的实现正在讨论中,API 可能不稳定,请谨慎使用。")]
3549
public static IAsyncCommandRunnerBuilder HandleException(this ICoreCommandRunnerBuilder builder, bool ignoreAllExceptions)
3650
{
37-
return builder.AsRunner().AddFallbackHandler(c => new CommandLineExceptionHandler(c.CommandLine, ignoreAllExceptions));
51+
return builder.AsRunner().AddFallbackHandler(new CommandLineExceptionHandlerMetadata(ignoreAllExceptions));
3852
}
3953
}

0 commit comments

Comments
 (0)