Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Build and Test

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
18 changes: 18 additions & 0 deletions CSharpExtender.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.CSharpExtender", "Test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkTestBench", "BenchmarkTestBench\BenchmarkTestBench.csproj", "{1CF1DAEF-0755-4F06-89CD-6ABCF50A838C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
ProjectSection(SolutionItems) = preProject
LICENSE.txt = LICENSE.txt
README.md = README.md
RELEASE_NOTES.md = RELEASE_NOTES.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{A344A16F-0891-4C85-AEB9-3974D4606C22}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{7B81E5AC-5679-484F-8757-6C3DE84DACBF}"
ProjectSection(SolutionItems) = preProject
.github\workflows\ci.yml = .github\workflows\ci.yml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -31,6 +45,10 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A344A16F-0891-4C85-AEB9-3974D4606C22} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{7B81E5AC-5679-484F-8757-6C3DE84DACBF} = {A344A16F-0891-4C85-AEB9-3974D4606C22}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1B310B14-9E81-497D-8284-40A19D9A89A4}
EndGlobalSection
Expand Down
16 changes: 15 additions & 1 deletion CSharpExtender/ExtensionMethods/LinqExtensionMethods.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using CSharpExtender.Services;
using System;
using System.Collections.Generic;
using System.Linq;

namespace CSharpExtender.ExtensionMethods;
Expand Down Expand Up @@ -41,6 +42,19 @@ public static void ForEach<T>(this IEnumerable<T> elements, Action<T> action)
}
}

/// <summary>
/// Returns a random element from the list.
/// </summary>
/// <typeparam name="T">The type of elements in the list.</typeparam>
/// <param name="options">The list to pick a random element from.</param>
/// <returns>A random element from the list, or default(T) if the list is empty.</returns>
public static T RandomElement<T>(this List<T> options)
{
return options.Count == 0
? default
: options[RngCreator.GetNumberBetween(0, options.Count - 1)];
}

/// <summary>
/// Checks if any element in the collection has a duplicate property value.
/// Ignores default values.
Expand Down
15 changes: 15 additions & 0 deletions CSharpExtender/Services/RngCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Security.Cryptography;

namespace CSharpExtender.Services;

/// <summary>
/// Class to create cryptographically random numbers
/// </summary>
public static class RngCreator
{
public static int GetNumberBetween(int minimumValue, int maximumValue)
{
return RandomNumberGenerator.GetInt32(minimumValue, maximumValue + 1);
}
}
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Extension methods and classes I often find useful for C# development.

![Build Status](https://github.com/ScottLilly/CSharpExtender/actions/workflows/ci.yml/badge.svg)
[![NuGet](https://img.shields.io/nuget/v/ScottLilly.CSharpExtender)](https://www.nuget.org/packages/ScottLilly.CSharpExtender/)
[![NuGet Downloads](https://img.shields.io/nuget/dt/ScottLilly.CSharpExtender)](https://www.nuget.org/packages/ScottLilly.CSharpExtender/)

## DataAnnotations

Attribute classes to validate properties in models.
Expand Down Expand Up @@ -74,7 +78,8 @@ This class provides LINQ-related extension methods in C#.
- **`HasDuplicatePropertyValue<T, TProperty>`**: Checks if any objects have the same value in the specified property.
- **`HasDuplicatePropertyValue<T>`**: Checks if any objects have the same value in the specified string property.
- **`None`**: Checks if none of the elements in the collection satisfy the provided condition. If no condition is provided, it checks if the collection is empty.

- **`RandomElement`**: Returns a random element from the list.
-
### NumericExtensionMethods

This class provides extension methods for numerical operations in C#.
Expand Down Expand Up @@ -178,10 +183,16 @@ Base class that inherits from ObservableModel (to handle property change notific

## Service classes

Classes to handle common tasks.
Static classes to handle common tasks.

### CompositeRegexMatcher

Accepts a list of regex patterns and checks if a string matches any of them.

-**`MatchesAny`**: Checks if the string matches any of the regex patterns.
-**`MatchesAny`**: Checks if the string matches any of the regex patterns.

### RngCreator

This class is designed to create cryptographically random numbers in C#.

- **`GetNumberBetween`**: Generates a cryptographically random number between the specified minimum and maximum values.
8 changes: 8 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# RELEASE NOTES

## Version 3.0.0 (2025-??-??)
### Breaking Changes
* Updated to .NET 8.0

### Features
* Improved performance for string extension methods.
16 changes: 16 additions & 0 deletions Test.CSharpExtender/ExtensionMethods/Test_LinqExtensionMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ public void ForEach_IEnumerableAction_AppliesAction()
Assert.Equal(15, sum);
}

[Fact]
public void RandomElement_ReturnsElementInList()
{
var list = new List<int> { 1, 2, 3, 4, 5 };
var randomElement = list.RandomElement();
Assert.Contains(randomElement, list);
}

[Fact]
public void RandomElement_EmptyList_ReturnsDefault()
{
var list = new List<int>();
var randomElement = list.RandomElement();
Assert.Equal(default, randomElement);
}

[Fact]
public void DetectDuplicatePropertyValues()
{
Expand Down
38 changes: 38 additions & 0 deletions Test.CSharpExtender/Services/Test_RngCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using CSharpExtender.Services;

namespace Test.CSharpExtender.Services;

public class Test_RngCreator
{
[Fact]
public void TestGetNumberBetween()
{
int min = 2;
int max = 12;
bool has2 = false;
bool has12 = false;

// Run 1000 times, to ensure we don't get a value outside of the range
for (int i = 0; i < 1000; i++)
{
int result = RngCreator.GetNumberBetween(min, max);

// Verify that the result is between 2 and 12
Assert.True(result >= min && result <= max);

// Check if 2 and 12 were generated at least once
if (result == 2)
{
has2 = true;
}
else if (result == 12)
{
has12 = true;
}
}

// Ensure that at least one 2 and one 12 were generated
Assert.True(has2);
Assert.True(has12);
}
}
Loading