Unfucked 0.0.1-beta.31
dotnet add package Unfucked --version 0.0.1-beta.31
NuGet\Install-Package Unfucked -Version 0.0.1-beta.31
<PackageReference Include="Unfucked" Version="0.0.1-beta.31" />
<PackageVersion Include="Unfucked" Version="0.0.1-beta.31" />
<PackageReference Include="Unfucked" />
paket add Unfucked --version 0.0.1-beta.31
#r "nuget: Unfucked, 0.0.1-beta.31"
#:package Unfucked@0.0.1-beta.31
#addin nuget:?package=Unfucked&version=0.0.1-beta.31&prerelease
#tool nuget:?package=Unfucked&version=0.0.1-beta.31&prerelease
🧰 Unfucked
Fix egregiously broken or missing functionality in .NET libraries. Provide useful boilerplate, façades, and polyfills. Inspired by underscore, lodash, Apache Commons, Spring, Guava, jQuery,Dojo, mootools, and Prototype.
Installation
dotnet add package Unfucked
using Unfucked;
Usage
Comparables
- Clip a value to a specified range (also known as clamping, limiting, and truncating).
int health = health.Clip(min: 0, max: 100);
Console
- Colored text and background in strings, not tightly coupled to
Console.Write.string colored = ConsoleControl.Color("Hello", Color.FromArgb(0xff, 0xaa, 0), Color.Black); ConsoleControl.WriteLine("Hello", Color.FromArgb(0xff, 0xaa, 0), Color.Black); ConsoleControl.Write("Hello", Color.FromArgb(0xff, 0xaa, 0), Color.Black); - Clear screen and move cursor.
ConsoleControl.Clear(); // clear screen and move to top-left corner ConsoleControl.ClearLine(); // clear line and move to left side - Enable colored output on Windows 10 1511 and later.
- Called implicitly whenever you call the
ConsoleControl.Color,Write,WriteLine,Clear, orClearLinemethods. - Can be manually checked and opportunistically enabled.
bool colorable = ConsoleControl.IsColorSupported();
- Called implicitly whenever you call the
Cryptography
- Random string generation
string randomString = Cryptography.GenerateRandomString(length: 20); - Is certificate temporally valid?
X509Certificate2 myCert; bool isValid = myCert.IsTemporallyValid(safetyMargin: TimeSpan.Zero, now: DateTime.Now);Only checks time validity, not trust chain, revocation, or algorithm weakness.
- Get certificate subject/issuer named part.
X509Certificate2 myCert; string? subjectCommonName = myCert.SubjectName.Get("CN"); string? issuerOrg = myCert.Issuer.Get("O");
Date and Time
- Absolute value of
TimeSpanwith a more discoverable name thatDuration, whose name doesn't imply absolute value at allTimeSpan absValue = TimeSpan.FromHours(-1).Abs() == TimeSpan.FromHours(1);
Decimal math
Mathoperations on 128-bitdecimalvaluesATanAcosAsinAtan2CalculateSinFromCosCosCoshExpIsIntegerIsSignOfSinePositiveLogLog10PowerPowerNSinSinhSqrtTanTanhTruncateToPeriodicInterval
Directories
- Delete directory without throwing an exception on missing directories.
bool found = Directories.TryDelete(directory: "myDir", recursive: false);
DNS
- Fluent resolving method
IPEndPoint? ipAddress = await new DnsEndPoint("aldaviva.com", 443).Resolve();- If you need to resolve anything other than an A or AAAA record, take a look at DnsClient.NET instead
Enumerables
- Filter out
nullvalues.IEnumerable<string?> sparseList = new List<string?> { "a", "b", null }; IEnumerable<string> list = withNulls.Compact(); // ["a", "b"] IDictionary<string, object?> sparseMap = new Dictionary<string, object?> { ["a"] = "AA", ["b"] = "BB", ["c"] = null }; IDictionary<string, object> map = sparseMap.Compact(); // { "a": "AA", "b": "BB" } - Add multiple values at once.
IList<string> list = ["a"]; list.AddAll("b", "c"); // ["a", "b", "c"] - Upsert into non-concurrent dictionary.
Dictionary<string, string> map = new() { ["a"] = "AA" }; string a = map.GetOrAdd(key: "a", value: "AA", out bool aAdded); // a == "AA", aAdded == false string b = map.GetOrAdd(key: "b", valueFactory: () => "BB", out bool bAdded); // b == "BB", bAdded == true- Value factories can also be asynchronous
- Fluent set difference method
ISet<string> original = new HashSet<string> { "a", "b", "c" }; ISet<string> subtract = new HashSet<string> { "a", "b" }; ISet<string> diff = original.Minus(subtract); // ["CC"] - Filter out consecutive duplicate values.
IEnumerable<string> original = ["a", "a", "b", "b", "c", "b"]; IEnumerable<string> deduped = original.DistinctConsecutive(); // ["a", "b", "c", "b"] - Convert
IAsyncEnumeratorto list.IAsyncEnumerator<string> enumerator = MyEnumerateAsync(); IReadOnlyList<string> enumerated = await enumerator.ToList(); - Get the first, last, singleton, or indexed item, or return
nullinstead ofdefaultfor value types, which are ambiguous with present elements.IEnumerable<TimeSpan> ts = [TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)]; TimeSpan? head = ts.FirstOrNull(); // 1 second TimeSpan? last = ts.LastOrNull(); // 2 seconds TimeSpan singleton = ts.SingleOrNull() ?? TimeSpan.FromSeconds(3); // 3 seconds TimeSpan t = ts.ElementAtOrNull(index: 100) ?? TimeSpan.FromSeconds(4); // 4 seconds - Get an element from a dictionary by key, or return
nullinstead ofdefault, because default value types are ambiguous with present elements. This avoids having to use an awkward, verbose TryGetValue in a ternary operator every time. Also includes methods for reference typed keys, becauseTryGetValuedid not exist in .NET Standard 2.0.var structDict = new Dictionary<string, int> { { "a", 0 } }; int? a = structDict.GetValueOrNullStruct("a"); // 0 int? b = structDict.GetValueOrNullStruct("b"); // null instead of 0, the default value of int, which is ambiguous with the presence of "b" var classDict = new Dictionary<string, string> { { "c", "0" } }; string? c = classDict.GetValueOrNull("c"); // "0" string? d = classDict.GetValueOrNull("d"); // null - Head and tail
IEnumerable<string> original = ["a", "b", "c"]; (string? head, IEnumerable<string> tail) = original.HeadAndTail(); // head == "a", tail == ["b", "c"]- If the item type is a value-type struct, use
HeadAndTailStruct()instead.
- If the item type is a value-type struct, use
- Delta changeset between two enumerables (created, updated, deleted, and unchanged items)
public record Person(long id, string name); IEnumerable<string> original = [new Person(1, "Alice"), new Person(2, "Bob"), new Person(3, "Charlie")]; IEnumerable<string> @new = [new Person(1, "Alice"), new Person(3, "Carol"), new Person(4, "Dan")]; (IEnumerable<Person> created, IEnumerable<Person> updated, IEnumerable<Person> deleted, IEnumerable<Person> unmodified) = original.DeltaWith(@new, person => person.id); // created contains Dan // updated contains Carol // deleted contains Bob // unmodified contains Alice- The original and new items don't have to be of the same type.
- Atomic swap on
ConcurrentDictionaryto upsert new value and get old valueConcurrentDictionary<string, ValueHolder<int>> map = Enumerables.CreateConcurrentDictionary(Singleton.Dictionary("a", 1)); int? oldValue = map.Swap(key: "a", newValue: 2); // oldValue == 1, map["a"] == 2 - Atomic compare and swap on
ConcurrentDictionaryto update new value if it exists and matches old value, and also get the old valueConcurrentDictionary<string, ValueHolder<int>> map = Enumerables.CreateConcurrentDictionary(Singleton.Dictionary("a", 1)); int? oldValue = map.CompareAndSwap(key: "a", oldValue: 1, newValue: 2); - Get or add to
ConcurrentDictionaryand determine whether a new value was added or an existing value was returnedConcurrentDictionary<long, Person> clients = new(); Person alice = clients.GetOrAdd(key: 1, newValue: new Person("Alice"), out bool aliceAdded); Person bob = clients.GetOrAdd(key: 2, valueFactory: id => new Person(id, "Bob"), out bool bobAdded); - Get or add to
ConcurrentDictionaryand dispose of created but unadded values.ConcurrentDictionary<string, HttpClient> clients = new(); HttpClient actual = clients.GetOrAddWithDisposal(key: "aldaviva.com", valueFactory: domain => new HttpClient { BaseAddress = domain }, out bool added); // if clients already contained the key "aldaviva.com", then // actual will be the existing client, // added will be false, and // any new HttpClient that may have been temporarily created for insertion before being discarded will be disposed // otherwise, actual will be a new HttpClient instance, and added will be true - Factories for singleton Dictionaries, Sets, and enumerables of key-value pairs
IReadOnlyDictionary<string, int> dict = Singleton.Dictionary(key: "a", value: 1); IReadOnlySet<string> set = Singleton.Set(item: "a"); IEnumerable<KeyValuePair<string, int>> kvs = Singleton.KeyValues(key: "a", value: 1); - Filter by exact type instead of by superclass.
public class Superclass; public class Subclass: Superclass; IEnumerable<object> a = [new Superclass(), new Subclass()]; IEnumerable<Superclass> superclasses = a.OfTypeExactly<Superclass>(); // superclasses does not contain any Subclass instances - Polyfill for
IList<T>.AsReadOnlyfor .NET versions before 8, including .NET StandardIReadOnlyList<string> ro = new List<string> { "a", "b" }.AsReadOnly();
Exceptions
- Get the chain of causes for an exception, which is a sequence of all of its inner exceptions, recursively. Excludes outermost exception.
Exception e; IEnumerable<Exception> causeChain = e.GetCauseChain(); // equivalent to [e.InnerException, e.InnerException.InnerException, ...] - Get the chain of messages for an exception, which is a string of all of its inner messages, recursively. Includes outermost message.
Exception e; string messages = e.MessageChain(includeClassNames: true); // like $"Exception: outer message; OtherExceptionClass: inner message; ..." - Determine if an
IOExceptionwas caused by a file already existing on Windows.try { Stream file = new FileStream("filename", FileMode.CreateNew, FileAccess.Write); } catch (IOException e) when (e.IsCausedByExistingWindowsFile()){ // filename already exists }- This case is undetectable on Linux and Mac OS, so it always returns
falsethere.
- This case is undetectable on Linux and Mac OS, so it always returns
Lazy
- Easily dispose of lazy value without all the conditional and exception handling boilerplate
Lazy<HttpClient> httpClientHolder = new(() => new HttpClient(), LazyThreadSafetyMode.PublicationOnly); httpClientHolder.TryDisposeValue();
Paths
- Trim trailing slashes
Paths.TrimTrailingSlashes(@"C:\Users\Ben\Desktop\") == @"C:\Users\Ben\Desktop" Paths.TrimTrailingSlashes("/home/ben/") == "/home/ben" - Create new empty temporary subdirectory in specific parent directory
Paths.CreateTempDir(); // example: %LOCALAPPDATA%\Temp\temp-12345678 Paths.CreateTempDir(Environment.ExpandEnvironmentVariables(@"%temp%\myapp")); // example: %LOCALAPPDATA%\Temp\myapp\temp-abcdefgh - Convert DOS backslashes to Unix forward slashes
Paths.Dos2UnixSlashes(@"C:\Users\Ben\Desktop") == "C:/Users/Ben/Desktop" - Match file extension against a set
IReadOnlySet<string> videoExtensions { get; } = new HashSet<string> { ".3gpp", ".asf", ".avi", ".bik", ".divx", ".dv", ".f4v", ".flv", ".m1v", ".m4v", ".mkv", ".mov", ".mp4", ".mp4v", ".mpeg", ".mpg", ".ts", ".vob", ".webm", ".wmv"}.ToFrozenSet(); bool hasVideoExt = Paths.MatchesExtensions("myfile.mp4", videoExtensions);
Processes
- Command line argument marshalling with correct escaping and quoting
- String to array (requires
Unfucked.Windowspackage)IEnumerable<string> argv = WindowsProcesses.CommandLineToEnumerable("arg1 arg2"); - Array to string
string args = Processes.CommandLineToString(["arg1", "'argument' \"2\""]);
- String to array (requires
- Run program and get output and exit code, like Node.js'
child_process.execFile()(int exitCode, string stdout, string stderr) result = await Processes.ExecFile("path/to/program.exe", ["arg1", "arg2"], extraEnv, "workDir", hideWindow: false, ct); - Determine whether the current program is a console or Windows GUI app.
bool isWindowsGuiProgram = Processes.IsWindowsGuiProgram();
Strings
- Coerce empty strings to
null"".EmptyToNull(); // null - Fluently check if a string has any non-whitespace characters
" ".HasText(); // false - Fluently check if a string has any characters
"".HasLength(); // false - Fluent join method
new[] { "A", "B" }.Join(", "); - Uppercase first letter
"ben".ToUpperFirstLetter(); // "Ben" - Lowercase first letter
"Ben".ToLowerFirstLetter(); // "ben" - Trim multiple strings from start, end, or both
"..::Ben::..".Trim(".", ":"); // "Ben" - Join enumerable into an English-style list with commas an a conjunction like
andnew[] { "Dewey", "Cheetum", "Howe" }.JoinHumanized(",", "and", true); // "Dewey, Cheetum, and Howe" - Fluent conversion method to byte array
"Ben".ToByteArray(new UTF8Encoding(false, true)); // [0x42, 0x65, 0x6E] - Fluent conversion method to byte stream
Stream stream = "Ben".ToByteStream(); - Convert DOS CRLF line breaks to Unix LF line breaks
"A\r\nB".Dos2Unix(); // "A\nB" - Repeat a string a certain number of times
"Ben".Repeat(4); // "BenBenBenBen" - Polyfills for
StringBuilder.AppendJoinin .NET Standard 2.0 - Polyfills for
string.StartsWith(char)andstring.EndsWith(char)in .NET Standard 2.0 - Polyfills for
string.Contains(string, StringComparison)in .NET Standard 2.0 - Polyfills for
string.Joinoverloads that takeReadOnlySpanin .NET Standard 2.0 and .NET Runtimes < 9
Tasks
- Unbounded delay time (.NET ≥ 6 tops out at 49.7 days, .NET < 6 tops out at 24.9 days)
await Tasks.Delay(TimeSpan.FromDays(365)); - Await multiple tasks and proceed when any of them both completes and the return value passes a predicate, or they all fail to complete or the predicate
- Return
trueif any passed orfalseif they all failedTask<string> a, b; bool any = await Tasks.WhenAny([a, b], s => s.Length > 1); - Return the first passing task's result, or
nullif they all failedTask<string> a, b; string? firstOrDefault = await Tasks.FirstOrDefault([a, b], s => s.Length > 1);
- Return
- Asynchronously await the cancellation of a
CancellationTokenwithout blocking the thread, which is especially important to prevent a deadlock if a CancellationToken is used to keep your main thread from exitingawait cancellationToken.Wait(); - Cancel a
CancellationTokenwhen the user presses <kbd>Ctrl</kbd>+<kbd>C</kbd>CancellationTokenSource cts = new(); cts.CancelOnCtrlC(); - Get the result of a task, or
nullif it threw an exception, to allow fluent null-coalescing to a fallback chain, instead of a temporary variable and multi-linetry/catchblock statementobject resultWithFallback = await Task.FromException<object>(new Exception()).ResultOrNullForException() ?? new object(); - Easily await tasks that may be null without having to add parentheses and null coalescing operators.
Task? myOptionalTask = null; await myOptionalTask.CompleteIfNull();
URIs
- Fluent method to get URL query parameters
string? value = new Uri("https://aldaviva.com?key=value").GetQuery()["key"]; - Builder pattern for URLs
Uri url = new UrlBuilder("https", "aldaviva.com") .Path("a") .Path("b/c") .QueryParam("d", "e") .QueryParam("f", "{f}") .ResolveTemplate("f", "g") .Fragment("h") .ToUrl(); // https://aldaviva.com/a/b/c?d=e&f=g#h - Truncate URIs to remove the fragment, query parameters, or path. Useful for getting the origin too.
Uri full = new("https://ben@aldaviva.com:443/path?key=value#hash"); full.Truncate(URI.Part.Query); // https://ben@aldaviva.com:443/path?key=value full.Truncate(URI.Part.Path); // https://ben@aldaviva.com:443/path full.Truncate(URI.Part.Authority); // https://ben@aldaviva.com:443/ full.Truncate(URI.Part.Origin); // https://aldaviva.com:443
XML
- Fluent methods to read an XML document from an HTTP response body as a mapped object, DOM, LINQ, or XPath
using HttpResponseMessage response = await new HttpClient().GetAsync(url); MyClass obj = await response.Content.ReadObjectFromXmlAsync<MyClass>(); XmlDocument dom = await response.Content.ReadDomFromXmlAsync(); XDocument linq = await response.Content.ReadLinqFromXmlAsync(); XPathNavigator xpath = await response.Content.ReadXPathFromXmlAsync(); - Find all descendant elements of a parent node which have a given tag name.
XDocument doc = XDocument.Parse(xmlString); IEnumerable<XElement> els = doc.Descendants("head", "body");
All Unfucked libraries
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. 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 is compatible. 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. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. 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.0
- Microsoft.Bcl.AsyncInterfaces (>= 8.0.0)
- System.Collections.Immutable (>= 8.0.0)
-
net6.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (6)
Showing the top 5 NuGet packages that depend on Unfucked:
| Package | Downloads |
|---|---|
|
Unfucked.HTTP
Fix egregiously broken or missing functionality in System.Net.Http.HttpClient. |
|
|
SolCalc
Find when sunrise, sunset, and different twilights happen for a given location, based on the NOAA ESRL Solar Calculator. Features high accuracy across several millenia, atmospheric refraction, a simple enumeration-based API, and multiple/missing events during polar night/day/twilight at extreme latitudes. |
|
|
Unfucked.DI
Fix egregiously broken or missing functionality in Microsoft.Extensions.Hosting dependency injection. |
|
|
Unfucked.Windows
Fix egregiously broken or missing functionality in .NET libraries that integrate with Windows APIs. |
|
|
Unfucked.STUN
Fix egregiously broken or missing functionality in Stun.Net. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.0.1-beta.31 | 300 | 12/7/2025 |
| 0.0.1-beta.30 | 172 | 11/25/2025 |
| 0.0.1-beta.29 | 428 | 11/20/2025 |
| 0.0.1-beta.28 | 363 | 11/20/2025 |
| 0.0.1-beta.27 | 361 | 11/19/2025 |
| 0.0.1-beta.26 | 352 | 11/17/2025 |
| 0.0.1-beta.25 | 2,481 | 11/11/2025 |
| 0.0.1-beta.24 | 176 | 11/9/2025 |
| 0.0.1-beta.23 | 118 | 11/9/2025 |
| 0.0.1-beta.22 | 90 | 11/8/2025 |
| 0.0.1-beta.21 | 90 | 11/8/2025 |
| 0.0.1-beta.20 | 231 | 11/6/2025 |
| 0.0.1-beta.19 | 145 | 11/2/2025 |
| 0.0.1-beta.18 | 168 | 10/28/2025 |
| 0.0.1-beta.17 | 160 | 10/27/2025 |
| 0.0.1-beta.16 | 163 | 10/22/2025 |
| 0.0.1-beta.15 | 176 | 10/20/2025 |
| 0.0.1-beta.14 | 180 | 10/19/2025 |
| 0.0.1-beta.13 | 331 | 9/25/2025 |
| 0.0.1-beta.12 | 634 | 7/23/2025 |
| 0.0.1-beta.11 | 549 | 7/23/2025 |
| 0.0.1-beta.10 | 197 | 7/13/2025 |
| 0.0.1-beta.9 | 262 | 7/3/2025 |
| 0.0.1-beta.8 | 172 | 6/29/2025 |
| 0.0.1-beta.7 | 169 | 6/29/2025 |
| 0.0.1-beta.6 | 175 | 6/26/2025 |
| 0.0.1-beta.5 | 306 | 6/5/2025 |
| 0.0.1-beta.4 | 146 | 6/5/2025 |
| 0.0.1-beta.3 | 215 | 5/21/2025 |
| 0.0.1-beta.2 | 183 | 5/9/2025 |
| 0.0.1-beta.1 | 255 | 4/12/2025 |
| 0.0.0-beta9 | 206 | 4/7/2025 |
| 0.0.0-beta8 | 508 | 3/11/2025 |
| 0.0.0-beta7 | 251 | 3/8/2025 |
| 0.0.0-beta6 | 329 | 3/6/2025 |
| 0.0.0-beta5 | 253 | 3/6/2025 |
| 0.0.0-beta4 | 326 | 1/20/2025 |
| 0.0.0-beta3 | 246 | 9/30/2024 |
| 0.0.0-beta2 | 184 | 9/8/2024 |
| 0.0.0-beta1 | 103 | 9/7/2024 |
| 0.0.0-alpha2 | 100 | 9/7/2024 |
| 0.0.0-alpha1 | 110 | 8/10/2024 |
