Framework for C# development.
ApplicationBase
Get information about the application: (non-exhaustive list)
string pathToExe = ApplicationBase.Path;
string myVersion = ApplicationBase.Version;
string processId = ApplicationBase.Process.Id;
string processElevated = ApplicationBase.Process.IsElevated;
Restart this process with elevated privileges. Environment.Exit will be invoked, after the new process was successfully started.
ApplicationBase.RestartElevated("", () => Environment.Exit(0));
CachedProperty
Load the contents of a property once and keep it cached:
public static CachedProperty<string> MyCachedFile { get; set; } = new(() =>
{
// The file is only read the first time when the getter is invoked.
return File.ReadAllText(@"C:\large_file.txt");
});
Optionally, specify a duration after which the value is invalidated and the getter will be invoked again:
public static CachedProperty<string> MyCachedFile { get; set; } = new(() =>
{
return File.ReadAllText(@"C:\large_file.txt");
}, TimeSpan.FromMinutes(10));
CSharp
Try / catch wrappers:
// Returns false, if an exception was thrown:
bool worked = CSharp.Try(() => MyFunction());
// Retrieves the string and null, if an exception was thrown:
string? value = CSharp.Try(() => RetrieveString());
// Retry 10 times:
UserDto user = CSharp.Retry(() => GetUser(1), 10);
Copy all properties of an object to an object of a different type:
UserDto userDto = CSharp.ConvertObject<UserDto>(userEntity, ConvertObjectOptions.IgnoreCase);
IndexedProperty & ReadOnlyIndexedProperty
Use IndexedProperty or ReadOnlyIndexedProperty to provide a property that has an indexer without the need to create a new class.
This indexed property can be backed by, e.g. a Dictionary, or the getter and setter can access underlying data from a custom source.
public ReadOnlyIndexedProperty<string, ConnectionString> ConnectionStrings { get; private set; } = new(name =>
{
// Getter
return GetConnectionStringByName(name);
});
ConnectionString myConnection = ConnectionStrings["Database1"];
public IndexedProperty<int, string> MyValueCollection { get; private set; } = new(id =>
{
// Getter
return "The value";
},
(id, value) =>
{
// Setter
// Store "value" under the index "id"
});
// Set
MyValueCollection[1] = "foo";
MyValueCollection[2] = "bar";
// Get
string fooString = MyValueCollection[1];
Blob & BlobTree
A Blob is a type with a name and a byte[] content:
byte[] content = ...
Blob blob = new("my_data", content);
// or:
Blob blob = Blob.FromFile(@"C:\file.txt")
A BlobTree is a tree structure, similar to a directory & file structure.
BlobTree tree = BlobTree.FromDirectory(@"C:\directory_name");
Helper method, like FromFile and FromDirectory exist, but blobs are generic data types and are not tied or limited to files or directories.
Some more helpers around blobs exist:
// Find "blobname" in a specified path within the BlobTree:
Blob blob = tree.FindBlob(@"path\to\node\blobname");
// Write entire BlobTree as it is to disk:
tree.SaveToDirectory(@"C:\target_path");
The ZipCompression class is an adapter between ZIP file compression and BlobCollection & BlobTree structures.
TreeNode
A TreeNode is a generic, hierarchical data structure. Each TreeNode has a value and children.
TreeNode<string> tree = new("My root node");
tree.Add("child node 1");
tree.Add("child node 2");
TreeNode<string> child3 = tree.Add("child node 3");
child3.Add("child node 3 child 1");
To construct a TreeNode statically, use TreeNodeBuilder:
TreeNode<string> tree = TreeNodeBuilder
.BeginTree("My root node")
.Begin("child node 1")
.End()
.Begin("child node 2")
.End()
.Begin("child node 3")
.Begin("child node 3 child 1")
.End()
.End()
.EndTree();
The TreeNode class has various methods for iteration to flatten, access ancestors, siblings, children, etc. A link to its parent node allows navigation up the tree.
Money & Currency
The Money datatype wraps an amount with a currency:
Money m = new(9.99, Currency.EUR);
With the CurrencyConverter and user-provided exchange rates, Money objects can be converted into other currencies:
CurrencyConverter converter = new()
.HasConversion(Currency.EUR, Currency.USD, 0.95)
.HasConversion(Currency.EUR, Currency.CHF, 1.05);
Money convertedValue = converter.Convert(m, Currency.USD);
ObservableObject
The ObservableObject class provides a base class for observable objects. This is especially relevant in WPF & MVVM.
public class MyDto : ObservableObject
{
public string Name { get; set => Set(ref field, value); }
}
The pattern is simple and does not bulge performance.
Extension Methods
This namespace contains extensions for all default types and several common .NET classes.
Here are some examples. However, there are many more extension methods and members.
// StringExtensions:
string subStr = "Hello, world!".SubstringFrom(","); // " world!"
string[] lines = "line 1\r\nline2\r\nline 3".SplitToLines();
int? maybeInt32Value = "123".ToInt32OrNull();
// ByteArrayExtensions:
bool arraysEqual = byteArrayA.Compare(byteArrayB);
int index = byteArrayA.FindSequence(byteArrayB);
Extensions are also available as extension members, which allows for a more natural syntax:
// DateOnlyExtensions:
DateOnly today = DateOnly.Today; // .net 10 extension member
DynamicLibrary
Dynamic invocation of DLL functions:
DynamicLibrary user32 = new("user32.dll");
DynamicLibraryFunction<int> function = user32.GetFunction<int>("GetTickCount", CallingConvention.StdCall, CharSet.Auto);
int ticks = function.Call();
HGlobal
Safe HGLOBAL Wrapper:
// Allocate HGLOBAL with 1024 bytes.
using (HGlobal mem = new(1024))
{
}
// Disposed!
Helper methods:
HGlobal mem = HGlobal.FromArray(byteArray);
byte[] array = mem.ToArray();
HGlobal memFromStruct = HGlobal.FromStructure(myStruct);
AlternateDataStreamInfo
Iterate alternate data streams of a file:
AlternateDataStreamInfo adsInfo = new(@"C:\file.txt");
foreach (AlternateDataStream ads in adsInfo.Streams)
{
Console.WriteLine(ads.Name);
Console.WriteLine(ads.ReadAllText());
}
BinaryStream
The BinaryStream combines the capabilities of BinaryReader and BinaryWriter and keeps track of the read and written byte count.
using (FileStream fileStream = File.OpenWrite(@"C:\file.txt"))
{
using BinaryStream stream = new(fileStream);
// Write to underlying stream
stream.Write(123);
stream.Write("foo");
stream.BaseStream.Seek(0, SeekOrigin.Begin);
// Read from underlying stream
int number = stream.ReadInt32();
string str = stream.ReadString();
}
CliCommand
Execute a file and retrieve the exit code and console output:
CliResult result = CliCommand
.FileName("netstat")
.Arguments("-o")
.Hidden()
.Execute();
int exitCode = result.ExitCode;
string consoleOutput = result.Output;
Compression
The Compression class offers a quick way to compress and decompress byte[] values:
byte[] compressed = Compression.Compress(data);
byte[] decompressed = Compression.Decompress(compressed);
TempDirectory
Creates a file in the system temp directory with the FileAttributes.Temporary attribute:
string path = TempDirectory.CreateFile("file.txt", byteArray);
// path = C:\Users\john\AppData\Local\Temp\{27666d85-2ab5-4c30-aa70-f00fc07f03e8}\file.txt
A subdirectory ensures that the path is unique and the file name does not need to be changed.
ZipCompression
The ZipCompression class compresses and decompresses ZIP archives from BlobTree objects:
// Decompress ZIP file into hierarchical structure:
BlobTree decompressed = ZipCompression.Decompress(@"C:\file.zip");
// Create ZIP file from hierarchical structure:
byte[] zipFile = ZipCompression.Compress(decompressed);
ByteSize
The ByteSize structure represents a size, in bytes:
ByteSize size = 10000;
// "9,77 KB"
string str = size.Format();
StringDistance
String distance algorithms:
int distance1 = StringDistance.Levenshtein("hello", "holla");
int distance2 = StringDistance.DamerauLevenshtein("hello", "holla");
Wording
String utility for linguistic text processing:
// "This is..."
string trimmedStr = Wording.TrimText("This is a very long sentence.", 10);
// "head, shoulders, knees and toes"
string joined = Wording.JoinStrings(", ", " and ", "head", "shoulders", "knees", "toes");
// Text where each line does not exceed 80 characters:
string wrappedTo80chars = Wording.WrapText("A whole paragraph with 1000 words [...]", 80, false);
- change: Targeting .NET 10.0
- new:
EnumerableExtensions.UnorderedEqualmethod - new:
RandomNumberGenerator.Sharedextension member - new:
DateOnly.Todayextension member - new:
TimeOnly.NowandUtcNowextension member - new:
Undefinableclass - change:
BitArrayExtensionsmethods changed to extension members:CountTrue,CountFalse,AllTrue,AllFalse - change:
BitCalculatormethods changed to extension members ofBitOperations - change:
ConvertExmethods changed to extension members ofConvert - change:
DateTimeExmethods changed to extension members ofDateTime - change:
DateOnlyExmethods changed to extension members ofDateOnly - change:
TimeOnlyExmethods changed to extension members ofTimeOnly - change:
DirectoryExmethods changed to extension members ofDirectory - change:
FileExmethods changed to extension members ofFile - change:
PathExmethods changed to extension members ofPath - change:
MathExmethods changed to extension members ofMath - change:
EnumExtensionsmethods changed to extension members ofEnum - change:
ExceptionExtensionsmethods changed to extension members:FullStackTrace,ChildExceptionCount - change:
ReflectionExtensionsmethods changed to extension members:NestedName,NestedFullName - change:
Create.HexadecimalStringmethod renamed toHexStringto match .net naming conventions - removed:
ConvertEx.ToHexadecimalStringandFromHexadecimalString
- change: Added
ParseExactparameter to JSON converters
- change: Targeting .NET 9.0
- new:
StringLengthJsonConverterclass - new:
BytecodeApi.Data.ObservableTupleclass - new:
BytecodeApi.Threading.CriticalSectionclass - removed:
CSharp.GetHashCodemethod - removed:
MathEx.Randomproperty - removed:
Network.DisableCertificateValidationandEnableAllSecurityProtocolsmethods.
- new:
CSharp.RunTaskmethod - new:
EnumerableExtensions.Unionmethod - new:
DateTimeEx,DateTimeExtensionsandDateOnlyExtensionsmethods
- new:
CliCommand.ExecuteAsyncmethod - removed:
StringExtensions.ReplaceLineBreaks - new:
DateTimeEx.IsValidDateandGetMonthsDifferencemethod
- new:
BytecodeApi.Data.TreeNodeclass - new:
DateOnlyExtensionsclass - new:
ReflectionExtensions.GetValuemethod overloads - new:
CliCommand.Executemethod overload - new:
MathEx.MinandMaxmethod overloads forDateOnlyandTimeOnly - new:
DateOnlyJsonConverterconstructor with format parameter - new:
TimeOnlyJsonConverterconstructor with format parameter - bugfix:
MathEx.InterpolatemapToValueRange parameter did not work correcly - removed:
EnumerableExtensions.SortandSortDescendingmethod - removed:
EnumEx.GetValuesmethod overload
- new:
RandomExtensions.NextEnumValuemethod - new:
RandomNumberGeneratorExtensions.GetEnumValuemethod - new:
RegistryExtensions.GetExpandStringValueandSetExpandStringValuemethod
- new:
SevenBitIntegerclass - removed:
ConvertEx.To7BitEncodedIntandFrom7BitEncodedInt
- Initial release
