diff --git a/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/FileRenameService.cs b/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/FileRenameService.cs
index 92f0e29311..fae51aabad 100644
--- a/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/FileRenameService.cs
+++ b/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/FileRenameService.cs
@@ -22,4 +22,13 @@ public static void ExecuteRenameFileWithFileClass()
Directory.Delete(directoryPath, true);
}
-}
\ No newline at end of file
+
+ public static string ChangeExtension(string filePath, string newExtension)
+ {
+ var newFilePath = Path.ChangeExtension(filePath, newExtension);
+
+ File.Move(filePath, newFilePath);
+
+ return newFilePath;
+ }
+}
diff --git a/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/FolderRenameService.cs b/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/FolderRenameService.cs
new file mode 100644
index 0000000000..3a2f14d059
--- /dev/null
+++ b/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/FolderRenameService.cs
@@ -0,0 +1,27 @@
+namespace RenameFilesWithCSharp;
+
+public static class FolderRenameService
+{
+ public static void RenameFolder(string oldFolder, string newFolder)
+ {
+ Directory.Move(oldFolder, newFolder);
+ }
+
+ public static void ExecuteRenameFolder()
+ {
+ var parentPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+
+ var oldFolder = Path.Combine(parentPath, "Invoices2025");
+ Directory.CreateDirectory(Path.Combine(oldFolder, "Q1"));
+ File.Create(Path.Combine(oldFolder, "Q1", "invoice-001.pdf")).Dispose();
+
+ var newFolder = Path.Combine(parentPath, "Invoices-2025");
+
+ RenameFolder(oldFolder, newFolder);
+
+ Console.WriteLine($"Renamed {Path.GetFileName(oldFolder)} to {Path.GetFileName(newFolder)}");
+ Console.WriteLine($"Invoice moved along: {File.Exists(Path.Combine(newFolder, "Q1", "invoice-001.pdf"))}");
+
+ Directory.Delete(parentPath, true);
+ }
+}
diff --git a/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/ImageRenameService.cs b/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/ImageRenameService.cs
index 011ed4cae4..eb4bc021d3 100644
--- a/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/ImageRenameService.cs
+++ b/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/ImageRenameService.cs
@@ -16,11 +16,45 @@ public static void RenameImagesWithDateTime(string directoryPath)
var fileExtension = Path.GetExtension(imageFile);
- var newFileName = $"Image_{creationTime:yyyy-MM-dd_HHmmss}{fileExtension}";
+ var baseName = $"Image_{creationTime:yyyy-MM-dd_HHmmss}";
- var newFilePath = Path.Combine(directoryPath, newFileName);
+ var newFilePath = Path.Combine(directoryPath, $"{baseName}{fileExtension}");
+
+ for (var copy = 1; File.Exists(newFilePath) && newFilePath != imageFile; copy++)
+ {
+ newFilePath = Path.Combine(directoryPath, $"{baseName}_{copy}{fileExtension}");
+ }
File.Move(imageFile, newFilePath);
}
}
-}
\ No newline at end of file
+
+ public static void RenameImagesWithDateTimeUsingFileInfo(string directoryPath)
+ {
+ var allowedExtensions = new[] { ".jpg", ".png", ".jpeg", ".gif" };
+
+ var imageFiles = Directory.EnumerateFiles(directoryPath)
+ .Where(file => allowedExtensions.Contains(Path.GetExtension(file),
+ StringComparer.OrdinalIgnoreCase));
+
+ foreach (var imageFile in imageFiles)
+ {
+ var file = new FileInfo(imageFile);
+
+ var creationTime = file.CreationTime;
+
+ var fileExtension = file.Extension;
+
+ var baseName = $"Image_{creationTime:yyyy-MM-dd_HHmmss}";
+
+ var newFilePath = Path.Combine(directoryPath, $"{baseName}{fileExtension}");
+
+ for (var copy = 1; File.Exists(newFilePath) && newFilePath != imageFile; copy++)
+ {
+ newFilePath = Path.Combine(directoryPath, $"{baseName}_{copy}{fileExtension}");
+ }
+
+ file.MoveTo(newFilePath);
+ }
+ }
+}
diff --git a/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/Program.cs b/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/Program.cs
new file mode 100644
index 0000000000..6489332579
--- /dev/null
+++ b/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/Program.cs
@@ -0,0 +1,6 @@
+using RenameFilesWithCSharp;
+
+FileFinderService.ExecuteFindFilesInFolder();
+FileRenameService.ExecuteRenameFileWithFileClass();
+FileInfoRenameService.ExecuteRenameFileWithFileInfoClass();
+FolderRenameService.ExecuteRenameFolder();
diff --git a/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/RenameFilesWithCSharp.csproj b/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/RenameFilesWithCSharp.csproj
index d81befcd75..238f157e2e 100644
--- a/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/RenameFilesWithCSharp.csproj
+++ b/files-csharp/RenameFilesWithCSharp/RenameFilesWithCSharp/RenameFilesWithCSharp.csproj
@@ -1,8 +1,8 @@
- Library
- net8.0
+ Exe
+ net10.0
enable
enable
-
\ No newline at end of file
+
diff --git a/files-csharp/RenameFilesWithCSharp/Tests/RenameFilesUnitTests.cs b/files-csharp/RenameFilesWithCSharp/Tests/RenameFilesUnitTests.cs
index c26b181b76..a112084fb2 100644
--- a/files-csharp/RenameFilesWithCSharp/Tests/RenameFilesUnitTests.cs
+++ b/files-csharp/RenameFilesWithCSharp/Tests/RenameFilesUnitTests.cs
@@ -97,4 +97,88 @@ public void WhenBatchRenamingPhotosWithDateTimeAndNoAllowedExtensionsExist_ThenN
// Assert
Assert.Equal(existingFiles, updatedFiles);
}
-}
\ No newline at end of file
+
+ [Fact]
+ public void WhenTwoPhotosShareACreationSecond_ThenBothAreRenamedWithoutCollision()
+ {
+ var first = Path.Combine(_tempDirectory, "a.jpg");
+ var second = Path.Combine(_tempDirectory, "b.jpg");
+ File.Create(first).Close();
+ File.Create(second).Close();
+ var sameSecond = new DateTime(2025, 6, 1, 10, 30, 0);
+ File.SetCreationTime(first, sameSecond);
+ File.SetCreationTime(second, sameSecond);
+
+ // Act
+ ImageRenameService.RenameImagesWithDateTime(_tempDirectory);
+
+ // Assert
+ var names = Directory.GetFiles(_tempDirectory).Select(f => Path.GetFileName(f)).Order(StringComparer.Ordinal).ToArray();
+ Assert.Equal(["Image_2025-06-01_103000.jpg", "Image_2025-06-01_103000_1.jpg"], names);
+ }
+
+ [Fact]
+ public void WhenBatchRenameRunsTwice_ThenSecondRunChangesNothing()
+ {
+ var sameSecond = new DateTime(2025, 6, 1, 10, 30, 0);
+ foreach (var name in new[] { "a.jpg", "b.jpg" })
+ {
+ var path = Path.Combine(_tempDirectory, name);
+ File.Create(path).Close();
+ File.SetCreationTime(path, sameSecond);
+ }
+ ImageRenameService.RenameImagesWithDateTimeUsingFileInfo(_tempDirectory);
+ var afterFirstRun = Directory.GetFiles(_tempDirectory).Order().ToArray();
+
+ // Act
+ ImageRenameService.RenameImagesWithDateTimeUsingFileInfo(_tempDirectory);
+
+ // Assert
+ Assert.Equal(afterFirstRun, Directory.GetFiles(_tempDirectory).Order().ToArray());
+ }
+
+ [Fact]
+ public void GivenExistingFolder_WhenRenamingFolder_ThenContentMovesWithIt()
+ {
+ var oldFolder = Path.Combine(_tempDirectory, "Invoices2025");
+ Directory.CreateDirectory(oldFolder);
+ File.WriteAllText(Path.Combine(oldFolder, "invoice-001.pdf"), "content");
+ var newFolder = Path.Combine(_tempDirectory, "Invoices-2025");
+
+ // Act
+ FolderRenameService.RenameFolder(oldFolder, newFolder);
+
+ // Assert
+ Assert.False(Directory.Exists(oldFolder));
+ Assert.True(File.Exists(Path.Combine(newFolder, "invoice-001.pdf")));
+ }
+
+ [Fact]
+ public void GivenTextFile_WhenChangingExtension_ThenOnlyExtensionChanges()
+ {
+ var filePath = Path.Combine(_tempDirectory, "notes.txt");
+ File.WriteAllText(filePath, "content");
+
+ // Act
+ var newFilePath = FileRenameService.ChangeExtension(filePath, ".md");
+
+ // Assert
+ Assert.Equal(Path.Combine(_tempDirectory, "notes.md"), newFilePath);
+ Assert.True(File.Exists(newFilePath));
+ Assert.False(File.Exists(filePath));
+ }
+
+ [Fact]
+ public void GivenExistingDestination_WhenMovingWithoutOverwrite_ThenIOException()
+ {
+ var source = Path.Combine(_tempDirectory, "a.txt");
+ var destination = Path.Combine(_tempDirectory, "b.txt");
+ File.WriteAllText(source, "new");
+ File.WriteAllText(destination, "old");
+
+ Assert.Throws(() => File.Move(source, destination));
+
+ File.Move(source, destination, overwrite: true);
+ Assert.Equal("new", File.ReadAllText(destination));
+ }
+}
diff --git a/files-csharp/RenameFilesWithCSharp/Tests/Tests.csproj b/files-csharp/RenameFilesWithCSharp/Tests/Tests.csproj
index ae5d67b16c..d3138e175e 100644
--- a/files-csharp/RenameFilesWithCSharp/Tests/Tests.csproj
+++ b/files-csharp/RenameFilesWithCSharp/Tests/Tests.csproj
@@ -1,19 +1,19 @@
- net8.0
+ net10.0
enable
enable
false
true
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
@@ -21,4 +21,4 @@
-
\ No newline at end of file
+