Sharpener.NamedResources 1.0.13

dotnet add package Sharpener.NamedResources --version 1.0.13
                    
NuGet\Install-Package Sharpener.NamedResources -Version 1.0.13
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Sharpener.NamedResources" Version="1.0.13" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sharpener.NamedResources" Version="1.0.13" />
                    
Directory.Packages.props
<PackageReference Include="Sharpener.NamedResources" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Sharpener.NamedResources --version 1.0.13
                    
#r "nuget: Sharpener.NamedResources, 1.0.13"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Sharpener.NamedResources@1.0.13
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Sharpener.NamedResources&version=1.0.13
                    
Install as a Cake Addin
#tool nuget:?package=Sharpener.NamedResources&version=1.0.13
                    
Install as a Cake Tool

Named Resources

To understand named resources, you should know the dilemma. And that comes from a niche application downstream.

The challenge

I work on solutions that utilize dependencies that are not always built from C#. Often C++. And so you don't get the common advantages of the framework for resilient code, like composition with interfaces, or generic types. Whether it's possible in C++ is not the point, it's that the immutable dependencies I work with don't.

There are reference types that can have a wide variety of members. For example, a geometric object with dimensions. Depending on the predetermined shape type, it could have a diameter, width, height, depth, length, angle, and so on. And it can have any combination of those.

The dependency allows me to write code like this:

extension(GeometryItem item)
{
    public double? GetDimension(string dimensionName)
    {
        // Often need to wrap in try catch because the interop can randomly cause data access failures that you
        // cannot predict
        try
        {
            var dimension = item.Dimensions.FirstOrDefault(dimension => dimension.Name.Equals(dimensionName, StringComparison.OrdinalIgnoreCase));
            if (dimension?.IsValidObject == true && dimension.StorageType == StorageType.Double || dimension.Value is double doubleValue) return doubleValue;
        }
        catch
        {
            // Track the failure, maybe.
        }
        return null;
    }
}

However, even after writing tooling like this, a larger application development efforts starts to really challenge you to avoid creating tech debt. Riddled throughout your application you'll start to see a whole lot of these:

var topExtension = item.GetDimension("Top Extension");
if(topExtension is null) return;

// or

var flatTop = item.GetOptionValue("Flat Top");

And this just gets repeated all over the place. Some of the option names are also quirky, like "Btm Width" instead of explicit "Bottom", or the regional dialect comes into play because someone from the UK built the dependency so it's a "Mitred" option for a corner instead of "Mitered".

You just want to define these string values once and be done with it! Ok, so what's wrong with this?

public static class Dimensions
{
    // document everything too dude
    public const string TopExtension = "Top Extension"
}

So I can write

var topExtension = item.GetDimension(Dimensions.TopExtension)

This is starting to look better, but it's more text to achieve a static value. Right? I mean, this is solid enough that, given no other options, I'd say this works. But I'm making a package here and that package aims to just improve life by that little bit.

So what does this package do?

The Solution

Start off by creating an enum with the values that you want to be able to lookup by.

 /// <summary>
 ///     The common dimensions found on items.
 /// </summary>
public enum DimensionType
{
    /// <summary>
    ///     The width dimension of an item.
    /// </summary>
    [Description(nameof(Width))]
    Width,
    /// <summary>
    ///     The top extension dimension of an item.
    /// </summary>
    [Description("Top Extension")]
    TopExtension
}

Then create an interface that inherits INamedResource.

public interface IDimensionName
{
}

Now decorate DimensionType to indicate that you want it to generate code for you to use, based on the enum.

 /// <summary>
 ///     The common dimensions found on items.
 /// </summary>
 [NamedResource(typeof(IDimensionName))]
public enum DimensionType

Now you do not need to create that static class with the strings in it.

Dimensions.TopExtension was created for you.

But something else was created for you too, and that is a static type. It is meant for making some more advanced signatures.

var topExtension = new TopExtension();

// This comes out as "Top Extension"
topExtension.Name;

Well, what am I gonna do with that?

extension(GeometryItem item)
{
    public double? Dim<TDim>() where TDim : IDimensionName, new() => item.GetDimension(new TDim().Name) ?? double.NaN;
    public double? GetDimension(string dimensionName)
    {
        ///
    }
}

And so now, my typing can be a little more succinct and strong.

var widthIsLarge = item.Dim<Width>() > 12;

// or

var isFlatTop = item.Option<OffsetDepth>() == Choices.FlatTop;

And what's nice is that the consistency between these two values

Dimensions.FlatTop
// And
new FlatTop().Name

Will be predictable and reliable, because they are sourced by the enum that you maintain, the descriptions you put on them, and the summaries you provide in the xml documentation.

Overrides

You can override the defaults as well.

The constant strings are written to Dimensions because it removes "Name" from IDimensionName end, then if there are two upper case letters in its typename, it removes the first, and lastly, it adds an "s" to the end.

[NamedResource(typeof(IDimensionName))]

// results in

Dims.Width == "Width"

The other optional attribute parameters govern the namespace that the generated values are placed in, which defaults to the same namespace as IDimensionName, and you can override the intellisense documentation for the Dimensions class output.

Product 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 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 was computed.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
1.0.13 0 6/25/2026
1.0.12 0 6/25/2026
1.0.10 9 6/25/2026
1.0.9 30 6/25/2026
1.0.8 28 6/25/2026