Skip to content

Commit 03c789e

Browse files
committed
Use Microsoft.IO over System.IO
When targeting .NET Framework, some commonly-used System.IO classes have worse performance than in .NET. They are also not null-annotated, which can allow bugs to slip through undetected. This change uses a global alias across the solution so that Path and Directory types come from the newer Microsoft.IO package/assembly. This gives us a reduction in allocations, access to newer methods (like Path.Join), and correct null annotations. All other changes here are in response to the user of the newer package, mostly around handling null values correctly.
1 parent b414946 commit 03c789e

File tree

21 files changed

+59
-23
lines changed

21 files changed

+59
-23
lines changed

Directory.Build.props

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@
143143
<Using Include="Microsoft.VisualStudio.Composition.ImportCardinality" Alias="ImportCardinality" />
144144
<!-- Prevent accidental use of Microsoft.VisualStudio.Threading.IAsyncDisposable -->
145145
<Using Include="System.IAsyncDisposable" Alias="IAsyncDisposable" />
146+
<!-- Enforce use of Microsoft.IO -->
147+
<Using Include="Microsoft.IO.Path" Alias="Path" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />
148+
<Using Include="Microsoft.IO.Directory" Alias="Directory" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />
149+
<Using Include="Microsoft.IO.SearchOption" Alias="SearchOption" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />
146150
</ItemGroup>
147151

148152
<Import Project="eng\imports\LanguageSettings.props" />

src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/IO/WindowsFileExplorer.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information.
22

33
using System.Runtime.InteropServices;
4-
using Path = Microsoft.IO.Path;
54

65
namespace Microsoft.VisualStudio.IO;
76

src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CreateFileFromTemplateService.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,20 @@ public async Task<bool> CreateFileAsync(string templateFile, string path)
3535
Requires.NotNull(templateFile);
3636
Requires.NotNullOrEmpty(path);
3737

38-
string directoryName = Path.GetDirectoryName(path);
3938
string fileName = Path.GetFileName(path);
39+
string? directoryName = Path.GetDirectoryName(path);
40+
41+
if (directoryName is null)
42+
{
43+
return false;
44+
}
4045

4146
string? templateLanguage = await GetTemplateLanguageAsync();
47+
4248
if (string.IsNullOrEmpty(templateLanguage))
49+
{
4350
return false;
51+
}
4452

4553
await _projectVsServices.ThreadingService.SwitchToUIThread();
4654

src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/Handlers/CompileItemHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ IEnumerable<string> GetFilePaths(BuildOptions options)
4343

4444
protected override void AddToContext(IWorkspaceProjectContext context, string fullPath, IImmutableDictionary<string, string> metadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger)
4545
{
46-
string[]? folderNames = FileItemServices.GetLogicalFolderNames(Path.GetDirectoryName(Project.FullPath), fullPath, metadata);
46+
string[]? folderNames = FileItemServices.GetLogicalFolderNames(Path.GetDirectoryName(Project.FullPath)!, fullPath, metadata);
4747

4848
logger.WriteLine("Adding source file '{0}'", fullPath);
4949
context.AddSourceFile(fullPath, isInCurrentContext: isActiveContext, folderNames: folderNames);

src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/FileMoveNotificationListener.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
using Microsoft.VisualStudio.Threading;
1212
// Debug collides with Microsoft.VisualStudio.ProjectSystem.VS.Debug
1313
using DiagDebug = System.Diagnostics.Debug;
14-
using Path = System.IO.Path;
1514
using static Microsoft.CodeAnalysis.Rename.Renamer;
1615

1716
namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename;
@@ -75,7 +74,13 @@ public async Task OnBeforeFilesMovedAsync(IReadOnlyCollection<IFileMoveItem> ite
7574
}
7675

7776
// Get the relative folder path from the project to the destination.
78-
string destinationFolderPath = Path.GetDirectoryName(_unconfiguredProject.MakeRelative(itemToMove.Destination));
77+
string? destinationFolderPath = Path.GetDirectoryName(_unconfiguredProject.MakeRelative(itemToMove.Destination));
78+
79+
if ( (destinationFolderPath is null))
80+
{
81+
continue;
82+
}
83+
7984
string[] destinationFolders = destinationFolderPath.Split(Delimiter.Path, StringSplitOptions.RemoveEmptyEntries);
8085

8186
// Since this rename only moves the location of the file to another directory, it will use the SyncNamespaceDocumentAction in Roslyn as the rename action within this set.

src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,14 @@ public override async Task RenameAsync(IProjectTreeActionHandlerContext context,
7878
Requires.NotNullOrEmpty(value);
7979

8080
string? oldFilePath = node.FilePath;
81-
string oldName = Path.GetFileNameWithoutExtension(oldFilePath);
81+
string? oldName = Path.GetFileNameWithoutExtension(oldFilePath);
8282
string newFileWithExtension = value;
8383
CodeAnalysis.Project? project = GetCurrentProject();
8484

8585
await CpsFileRenameAsync(context, node, value);
8686

87-
if (project is null ||
87+
if (oldName is null ||
88+
project is null ||
8889
await IsAutomationFunctionAsync() ||
8990
node.IsFolder ||
9091
_vsOnlineServices.ConnectedToVSOnline ||

src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Retargeting/DotNetReleasesProvider.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using Microsoft.VisualStudio.Linq;
55
using Microsoft.VisualStudio.Shell.Interop;
66
using Microsoft.VisualStudio.Threading;
7-
using Path = Microsoft.IO.Path;
87

98
namespace Microsoft.VisualStudio.ProjectSystem.VS.Retargeting;
109

src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Retargeting/ProjectRetargetHandler.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Microsoft.VisualStudio.Shell.Interop;
77
using Microsoft.VisualStudio.Threading;
88
using IFileSystem = Microsoft.VisualStudio.IO.IFileSystem;
9-
using Path = Microsoft.IO.Path;
109

1110
namespace Microsoft.VisualStudio.ProjectSystem.VS.Retargeting;
1211

src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Setup/DotNetEnvironment.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public bool IsSdkInstalled(string sdkVersion)
6969
registryKey,
7070
"InstallLocation");
7171

72-
if (!string.IsNullOrEmpty(installLocation))
72+
if (!Strings.IsNullOrEmpty(installLocation))
7373
{
7474
string dotnetExePath = Path.Combine(installLocation, "dotnet.exe");
7575
if (_fileSystem.FileExists(dotnetExePath))

src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsChangeTracker.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,12 @@ internal void ProcessDataflowChanges(IProjectVersionedValue<ValueTuple<DesignTim
196196
// Make sure we have the up to date output path. If either of these don't exist, they will be null and we'll handle the ArgumentException below
197197
string? basePath = configChanges.After.Properties.GetValueOrDefault(ConfigurationGeneral.ProjectDirProperty);
198198
string? objPath = configChanges.After.Properties.GetValueOrDefault(ConfigurationGeneral.IntermediateOutputPathProperty);
199+
200+
if (basePath is null || objPath is null)
201+
{
202+
return null;
203+
}
204+
199205
try
200206
{
201207
tempPEOutputPath = Path.Combine(basePath, objPath, "TempPE");

0 commit comments

Comments
 (0)