Skip to content

Commit fa06ab9

Browse files
committed
fix: lazy load command object types to reduce unnecessary allocations
1 parent 61072e4 commit fa06ab9

File tree

6 files changed

+56
-7
lines changed

6 files changed

+56
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
[Unreleased changes](https://github.com/natemcmaster/CommandLineUtils/compare/v2.5.0...HEAD):
44

5+
## [v2.5.1](https://github.com/natemcmaster/CommandLineUtils/compare/v2.5.0...v2.5.1)
6+
7+
* Fix [#320] - lazy load command object types to reduce unnecessary allocations
8+
59
## [v2.5.0](https://github.com/natemcmaster/CommandLineUtils/compare/v2.4.4...v2.5.0)
610

711
* Fix [#92] by [@kbilsted] - Show enum names in help text for Options and Arguments stored as enum

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
</PropertyGroup>
4747

4848
<PropertyGroup>
49-
<VersionPrefix>2.5.0</VersionPrefix>
49+
<VersionPrefix>2.5.1</VersionPrefix>
5050
<VersionSuffix>rc</VersionSuffix>
5151
<IncludePreReleaseLabelInPackageVersion Condition="'$(IsStableBuild)' != 'true'">true</IncludePreReleaseLabelInPackageVersion>
5252
<BuildNumber Condition=" '$(BuildNumber)' == '' ">$(BUILD_NUMBER)</BuildNumber>

src/CommandLineUtils/CommandLineApplication{T}.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,9 @@ public Func<TModel> ModelFactory
115115
}
116116

117117
/// <inheritdoc />
118+
// TODO remove in 3.0. This doesn't do anything anymore.
118119
protected override void HandleParseResult(ParseResult parseResult)
119120
{
120-
(this as IModelAccessor).GetModel();
121-
122121
base.HandleParseResult(parseResult);
123122
}
124123

@@ -127,7 +126,7 @@ protected override void HandleParseResult(ParseResult parseResult)
127126
/// <inheritdoc />
128127
public override void Dispose()
129128
{
130-
if (Model is IDisposable dt)
129+
if (_lazy.IsValueCreated && Model is IDisposable dt)
131130
{
132131
dt.Dispose();
133132
}

src/CommandLineUtils/releasenotes.props

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ Features and bug fixes:
77
* @Alxandr: Add support for private base type options
88
* @AndreGleichner: Update generated help to display [command] first then [options]
99
* @daveMueller: Fix generated help to display the help options correctly
10+
11+
Patch 2.5.1:
12+
* Lazy load command object types to reduce unnecessary allocations
1013
</PackageReleaseNotes>
1114
<PackageReleaseNotes Condition="$(VersionPrefix.StartsWith('2.4.'))">
1215
Features and bug fixes by some awesome contributors:

test/CommandLineUtils.Tests/CommandLineApplicationExecutorTests.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ public void OnExecute()
293293
public void DisposesSubCommands()
294294
{
295295
var ex = Assert.Throws<InvalidOperationException>(
296-
() => CommandLineApplication.Execute<ParentCommand>(NullConsole.Singleton, "sub"));
296+
() => CommandLineApplication.Execute<ParentCommand>(NullConsole.Singleton, "disposable"));
297297
Assert.Equal("Hello", ex.Message);
298298
}
299299

@@ -312,8 +312,12 @@ public void Dispose()
312312
[Command("sub")]
313313
private class Subcommand
314314
{
315+
public DisposableParentCommand Parent { get; }
316+
315317
public void OnExecute()
316-
{ }
318+
{
319+
Assert.NotNull(Parent);
320+
}
317321
}
318322

319323
[Fact]

test/CommandLineUtils.Tests/CommandLineApplicationOfTTests.cs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Copyright (c) Nate McMaster.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using Xunit;
56
using Xunit.Abstractions;
7+
using Xunit.Sdk;
68

79
namespace McMaster.Extensions.CommandLineUtils.Tests
810
{
@@ -75,14 +77,51 @@ public void ThrowsForArgumentsWithoutMatchingAttribute()
7577
private class TestClass
7678
{
7779
public TestClass(string _) { }
80+
81+
public void OnExecute() { }
7882
}
7983

8084
[Fact]
8185
public void ThrowsForNoParameterlessConstructor()
8286
{
83-
var app = new CommandLineApplication<TestClass>();
87+
var app = new CommandLineApplication<TestClass>(new TestConsole(_output));
88+
app.Conventions.UseOnExecuteMethodFromModel();
8489
var exception = Assert.Throws<MissingParameterlessConstructorException>(() => app.Execute());
8590
Assert.Equal(typeof(TestClass), exception.Type);
8691
}
92+
93+
[Subcommand(typeof(SimpleCommand))]
94+
class ThrowsInCtorClass
95+
{
96+
public ThrowsInCtorClass()
97+
{
98+
throw new XunitException("Parent comand object should not be initialized.\n" + Environment.StackTrace);
99+
}
100+
101+
public void OnExecute() { }
102+
}
103+
104+
[Fact]
105+
public void ItDoesNotInitalizeClassUnlessNecessary()
106+
{
107+
using var app = new CommandLineApplication<ThrowsInCtorClass>(new TestConsole(_output));
108+
app.Conventions.UseDefaultConventions();
109+
var parseResult = app.Parse();
110+
Assert.NotNull(parseResult);
111+
Assert.Same(app, parseResult.SelectedCommand);
112+
}
113+
114+
class SimpleCommand
115+
{
116+
public int OnExecute() => 2;
117+
}
118+
119+
[Fact]
120+
public void ItDoesNotInitalizeParentClassUnlessNecessary()
121+
{
122+
using var app = new CommandLineApplication<ThrowsInCtorClass>(new TestConsole(_output));
123+
app.Conventions.UseDefaultConventions();
124+
Assert.Equal(2, app.Execute("simple"));
125+
}
87126
}
88127
}

0 commit comments

Comments
 (0)