BlazingDev.BlazingExtensions
0.1.0
Prefix Reserved
dotnet add package BlazingDev.BlazingExtensions --version 0.1.0
NuGet\Install-Package BlazingDev.BlazingExtensions -Version 0.1.0
<PackageReference Include="BlazingDev.BlazingExtensions" Version="0.1.0" />
paket add BlazingDev.BlazingExtensions --version 0.1.0
#r "nuget: BlazingDev.BlazingExtensions, 0.1.0"
// Install BlazingDev.BlazingExtensions as a Cake Addin #addin nuget:?package=BlazingDev.BlazingExtensions&version=0.1.0 // Install BlazingDev.BlazingExtensions as a Cake Tool #tool nuget:?package=BlazingDev.BlazingExtensions&version=0.1.0
BlazingExtensions
Extensions for every kind of C# projects - not only for Blazor!
Hello C# developers!
Within this repo I'm collecting all the extensions that I was missing over and over in my daily work. Hopefully they will also speed up and inspire your life 😃
If you have the possibility to make yourself 5% more productive and your code 5% more readable and bug-free, then you should definitely use it!
So let's start into your blazing future 😉
String extensions
.HasContent()
Are you happy using string.IsNullOrEmpty(value)
all the time? Or was it string.IsNullOrWhiteSpace(value)
?
And shouldn't you write positive code according to "Clean Code"?
Have you ever forgotten to invert the return value when using it within an if
statement?
Clear the stage for value.HasContent()
! 🎉
What will it return?
false
for everything useless likenull
,""
," "
,"\t"
,"\n"
," \t \n "
true
for useful content like"x"
or" hello\n"
So no rocket science, actually just the positive version of string.IsNullOrWhiteSpace()
🤷\
.Fallback(otherString)
The ??
operator is cool but only works for null
values. If you also want to fall back on empty / whitespace /
useless strings, use this extension.
Imagine you want to display some short preview text for a User
class. Would your code look as simple like this?
public string GetPreviewText()
{
return NickName
.Fallback(FirstName)
.Fallback(LastName)
.Fallback(Email)
.Fallback("User #" + Id);
}
.EqualsIgnoreCase(other)
/ .ContainsIgnoreCase(subString)
/ .StartsWithIgnoreCase(subString)
/ .EndsWithIgnoreCase(subString)
Just shorter versions of:
myString.Equals(other, StringComparison.OrdinalIgnoreCase)
myString.Contains(subString, StringComparison.OrdinalIgnoreCase)
myString.StartsWith(subString, StringComparison.OrdinalIgnoreCase)
myString.EndsWith(subString, StringComparison.OrdinalIgnoreCase)
.
And because they pop up in Intellisense you get reminded to think about casing 😉
.TrimStartOnce(trimValue)
Trims substrings, not just characters.
"info:job done".TrimStartOnce("info:"); // returns: "job done"
"info:info:job done".TrimStartOnce("info:"); // returns: "info:job done"
.TrimEndOnce(trimValue)
Trims substrings, not just characters.
"file1.txt".TrimEndOnce(".txt"); // returns: "file1"
.Truncate(maxLength)
Fast and convenient way to shorten a given text, e.g. for a preview line.
"hello".Truncate(5); // returns "hello", no truncation needed
"how are you?".Truncate(5); // returns "how a"
null.Truncate(5); // returns "", because nobody likes nulls
.Ellipsis(maxLengthIncludingEllipsis, ellipsisText)
Fast and convenient way to shorten a given text + appending ellipsis text, e.g. for a preview line.
The default ellipsis text …
only uses 1 character.
"hello".Ellipsis(5); // returns "hello", no ellipsis needed
"how are you?".Ellipsis(5); // returns "how …"
null.Ellipsis(5); // returns "", because nobody likes nulls
// you can specify your own ellipsis text
"hello beautiful world".Ellipsis(15, " [more]"); // returns "hello be [more]"
.SwapText(text1, text2)
Returns a string where text1
is swapped with text2
and text2
is swapped with text1
.
"Anna knows Bob".SwapText("Anna", "Bob"); // returns "Bob knows Anna"
if (preferInformalGreeting)
{
return GetFormalGreetingLine().SwapText(person.FirstName, person.LastName);
}
This would not work:
"Anna knows Bob".Replace("Anna", "Bob").Replace("Bob", "Anna"); // would be "Anna knows Anna"
.BzSplit(separator)
Handy splitter function which also
- removes empty entries (including whitespace ones)
- trims entries
"hello, , world ,,,".BzSplit(',') // returns ["hello","world"]
items.StringJoin(separator)
Convenient way to join a list together just like string.Join(separator, items)
.
Bonus point: allows the items list to be null
(returns ""
then).
Tip: to exclude null
items INSIDE the list, use .WhereNotNull()
(see below).
Example:
var nextThreeSongs = playlistItems.Take(3).Select(x => x.SongName).StringJoin(", ");
Numeric extensions
someDouble.ToInvariantString()
Useful when generating HTML/CSS styles and the user has a non-english culture.
<progress value="@percentage.ToInvariantString()" max="1"></progress>
DateTime extensions
.ToMidnight()
Moves the DateTime
's time portion to 23:59:59.999
. Useful for data filtering:
reportFrom = selectedDay.Date; // just to get sure you don't have any time portion
reportTo = reportFrom.ToMidnight(); // call the extension method upfront if you use EntityFramework
var singleDayDataQuery = dataFromDb.Where(x => x.Timestamp >= reportFrom && x.Timestamp <= reportTo);
.ToStartOfMonth()
Moves a given DateTime
to the first day of its month and sets the time to 00:00:00.000
. Useful for data filtering.
See code example at .ToEndOfMonth()
below.
.ToEndOfMonth()
Moves a given DateTime
to the last day of its month and sets the time to 23:59:59.999
. Useful for data filtering:
// imagine you need to always fetch a full month here
reportFrom = reportFrom.ToStartOfMonth(); // call the extension method upfront if you use EntityFramework
reportTo = reportFrom.ToEndOfMonth();
return dataFromDb.Where(x => x.Timestamp => reportFrom && x.Timestamp <= reportTo);
.ToJsTicks()
Returns the JavaScript ticks for a given DateTime
.
Info: If the DateTimeKind
is Local
, the value is converted to UTC
before calculating the JS ticks.
var xAxisValuesForSomeChart = rawData.Select(x => x.Timestamp.ToJsTicks());
.IfUndefinedSpecifyKind(kind)
Simple way to specify the DateTimeKind
for a DateTime
if it's current Kind
is Unspecified
.
This is different from calling .ToUniversalTime()
or .ToLocalTime()
on Unspecified
DateTimes because there the framework assumes that you desire a conversion and applies the timezone offset.
// assume timestamps are UTC
someData.ForEach(x => x.Timestamp = x.Timestamp.IfUndefinedSpecifyKind(DateTimeKind.Utc));
IComparable<T> extensions
Almost every struct is IComparable<T>
, e.g. double, int, DateTime, TimeSpan, ...
.Clamp(minValue, maxValue)
Ever had the need to clamp a value into a given range? Was it Math.Max(minValue, Math.Min(maxValue, userInput))
? Looks
quite difficult...
Much easier:
userInput = userInput.Clamp(minValue, maxValue);
volume = volume.Clamp(0, 10);
percentage = percentage.Clamp(0, 1);
bookingTime = bookingTime.Clamp(options.OpeningTime, options.ClosingTime);
It's even possible to only limit one part:
startPosition = startPosition.Clamp(0, null);
amount = amount.Clamp(null, user.MaxAmount);
.IsBetweenInclusive(lowerLimit, upperLimit)
What's the similarity between if (value >= 10 && value <= 20)
and if (10 <= value && value <= 20)
?
IMHO they are both quite difficult to read. You need some time and mental effort to check if the comparison is made the
right
way.
What about:
if (value.IsBetweenInclusive(10, 20))
? 😃
.IsBetweenExclusive(lowerLimit, upperLimit)
Same as above, but the argument numbers itself are not considered "valid".
10.IsBetweenExclusive(10, 20) // returns false
Enum extensions
.GetDescription()
Returns the value of the [Description("...")]
attribute and falls back to simple .ToString()
.
public enum MyEnum
{
Download,
Skip,
[Description("Download later")]
DownloadLater
}
MyEnum.Skip.GetDescription(); // returns "Skip"
MyEnum.DownloadLater.GetDescription(); // returns "Download later"
BzEnumX.Parse<TEnum>(text)
Parses the given text
to the desired TEnum
. Also checks for [Description]
attribute matches.
Throws ArgumentException
if parsing was not possible.
public enum MyEnum
{
FirstEntry = 0,
[Description("Second entry")] SecondEntry = 1,
ThirdEntry = 2
}
// all of the following lines work:
BzEnumX.Parse<MyEnum>("SecondEntry");
BzEnumX.Parse<MyEnum>("Second entry");
BzEnumX.Parse<MyEnum>("1");
BzEnumX.Parse<MyEnum>("secondentry", ignoreCase: true);
BzEnumX.Parse<MyEnum>("second ENTRY", ignoreCase: true);
BzEnumX.Parse<MyEnum>("1", ignoreCase: true);
// and it also works for [Flags] enums!
.RemoveFlag(flagToRemove)
/ .AddFlag(flagToAdd)
/ .SetFlag(flag, set)
Easier to read than:
flags = flags | MyFlagEnum.ThirdFlag
flags |= MyFlagEnum.ThirdFlag
flags = flags & ~MyFlagEnum.ThirdFlag
flags &= ~MyFlagEnum.ThirdFlag
var everythingFromInstances = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
var everythingStatic = everythingFromInstances.RemoveFlag(BindingFlags.Instance).AddFlag(BindingFlags.Static);
var staticBindingFlagsWithUserSelection = everythingStatic.SetFlag(BindingFlags.NonPublic, shouldIncludePrivateMembers);
Info! Don't expect too much performance when calling this method very frequently as a lot of conversion is needed!
IEnumerable<T> extensions
.HasContent()
Returns true
if the collection has items (and therefore is not null
).
Prevents you from unreadable (and potentially buggy) code like:
if (myItems?.Any() == true)
if (myItems?.Any() != false)
(does not work fornull
collections)if (myItems?.Any() ?? false)
if ((myItems?.Any()).GetValueOrDefault())
if (myItems?.Count() > 0)
(could be slow)
Use !myItems.HasContent()
to prevent yourself from unreadable (and potentially buggy) code like:
if (!myItems.Any())
if (myItems.Any() == false)
if (myItems.Any() != true)
if (myItems.Count() == 0)
if (myItems?.Any() == false)
(which does not work fornull
collections)if (myItems?.Any() != true)
if (myItems?.Count() == 0)
(which does not work fornull
collections and could be slow)
.WhereNotNull()
Returns only non-null items with the correct nullability information so you don't get compiler warnings.
foreach (var item in data.WhereNotNull())
{
// no compiler warning here
DoSomething(item.Id, item.Name);
}
When used with structs, they are even unpacked!
someData
.Select(x => x.UpdateTimestamp)
.WhereNotNull()
// now direct value access is possible
.Select(x => x.DayOfWeek)
// ...
.ForEach(action)
Ever tried to call .ForEach(...)
on an Array
, IEnumerable<T>
or HashSet<T>
? Now you can!
Without calling .ToList()
upfront!
someItems.ForEach(x => DoSomeFurtherProcessing(x));
.OrderByFirstWhere()
and .ThenByFirstWhere()
Useful when you want to have some pinned / favorite / urgent items on the top of a list.
return tasks
.OrderByFirstWhere(x => x.Pinned)
.ThenByFirstWhere(x => x.IsUrgent)
.ThenBy(/* whatever you usually sort on */);
Type extensions
.IsNumeric()
returns true for numeric types: byte
, sbyte
, short
, ushort
, int
, uint
, long
, ulong
, float
, double
, decimal
.
typeof(int).IsNumeric(); // returns true
var numericProperties = someObject
.GetType()
.GetProperties()
.Where(x => x.PropertyType.IsNumeric());
.UnwrapNullable()
Unwraps a potential nullable type. Or just returns the input type.
typeof(int?).UnwrapNullable(); // returns typeof(int)
typeof(int).UnwrapNullable(); // returns typeof(int)
typeof(StringBuilder).UnwrapNullable(); // returns typeof(StringBuilder)
Utilities
BzDisposeAction
A very simple IDisposable
implementation which just invokes the passed action on manual .Dispose()
call or when
reaching the end of an using(...)
statement.
Example:
private void HandleButtonClick()
{
_isButtonEnabled = false;
using(new BzDisposeAction(() => _isButtonEnabled = true))
{
DoSomethingDangerousThatMayThrowAnException();
}
// the compiler creates a "finally" block here for you so the "button enabling" action will get invoked
}
BzAsyncDisposeAction
Same as above, but for async actions/disposals. You must use the await using(...)
statement.
Example:
private async Task ListenForMessagesAsync()
{
var subscription = await someService.CreateSubscriptionAsync(someTopics);
await using(new BzAsyncDisposeAction(() => subscription.UnsubscribeAsync()))
{
// await Task.Delay...
// await DoSomethingAsync()...
// await someTaskCompletionSource.Task...
}
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.1.0 | 194 | 6/22/2024 |