Emik.SourceGenerators.TheSquareHole 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Emik.SourceGenerators.TheSquareHole --version 1.0.0                
NuGet\Install-Package Emik.SourceGenerators.TheSquareHole -Version 1.0.0                
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="Emik.SourceGenerators.TheSquareHole" Version="1.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Emik.SourceGenerators.TheSquareHole --version 1.0.0                
#r "nuget: Emik.SourceGenerators.TheSquareHole, 1.0.0"                
#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.
// Install Emik.SourceGenerators.TheSquareHole as a Cake Addin
#addin nuget:?package=Emik.SourceGenerators.TheSquareHole&version=1.0.0

// Install Emik.SourceGenerators.TheSquareHole as a Cake Tool
#tool nuget:?package=Emik.SourceGenerators.TheSquareHole&version=1.0.0                

Emik.SourceGenerators.TheSquareHole

NuGet package License

icon.png

"And up next, a cylinder. Hmm, I think that goes in... the square hole!"

Adds structural typing to C#. Made as a celebration for my 20th birthday on August 8th 2023.

This project has a dependency to Emik.Morsels, if you are building this project, refer to its README first.



Why

Despite the presentation of the project, the project itself is treated very seriously and addresses a real-world problem.

When creating software, you are encouraged to write code that is easily extensible and reusable. Interfaces are a fantastic way of achieving this, often allowing you to reuse methods that take said interface as a parameter. Part of the problem however is having to juggle the signatures of each interface in your head.

If you are creating a type specifically to implement an interface, this isn't much of a problem, but if you want to maximize the re-usability of a type, particularly if you make a type whose purpose is fairly generic, then you may often sit thinking about each interface you may consider adding.

Languages like Scala solve this issue using Structural Typing. If a type has the same declaring members as an interface, then it derives it.

How

This source generator looks at every interface accessible from your assembly and determines whether it is able to implement the interface.

Empty interfaces, alongside attributes marked with ObsoleteAttribute are not considered part of the search, as these interfaces tend to function more like attributes, and should therefore be opt-in.

If the interface contains generics, then the source generator performs Type Substitution: It considers every type declared within any type, including itself. For instance, take a look at the following type:

public class A(int i);

The type A declares a method with a parameter int, with nothing else. Therefore the candidates are int and A.

This results in the source generator being extremely flexible, and accounts for every possible implementation. For instance:

public partial record Cylinder<T1, T2>(T1 Second)
{
    public int First => 0;
}

interface ISquare<out T1, out T2>
{
    T1 First { get; }

    T2 Second { get; }
}

...generates...

// <auto-generated/>
#nullable enable
partial record Cylinder<T1, T2> : global::ISquare<int, T1>,
    global::System.Numerics.IEqualityOperators<global::Cylinder<T1, T2>, global::Cylinder<T1, T2>, bool>
{
}

The hard upper limit for type substitution with generics are exactly 3 of them. This does not include generics which are self-constrained.

While this analyzer does perform a lot of tricks to squeeze performance, type substituion may be still too expensive on your machine. Refer to Configure in that case.

Configure

Use .editorconfig/.globalconfig to configure this source generator:


Option the_square_hole_enable_concurrency
Summary Determines whether to enable concurrency for inspections.
Remarks Concurrency is only faster in large projects due to overhead in initializing concurrent behavior, hence why it's disabled by default.
Type bool
Default false

Option the_square_hole_include_nullability
Summary Determines whether or not to include nullability as a restriction.
Remarks A(string s) and A(string? s) are equal interface-wise despite the unequal signature metadata. This can however violate the contract of the interface, and you will get the suggestion to change the signature, which would be a breaking feature in an existing API.
Type bool
Default false

Option the_square_hole_include_parameter_name
Summary Determines whether or not to include parameter names as a restriction.
Remarks A(int a) and A(int b) are equal interface-wise despite the unequal parameter naming. Some analyzers encourage renaming parameters when such a scenario occurs, which would be a breaking feature in an existing API.
Type bool
Default false

Option the_square_hole_max_substitution_depth
Summary Determines the maximum number of type substitutions allowed for a given interface.
Remarks Lower = faster, higher = better inference of generics. Lower it if you face performance issues.
Type 0..=3
Default 3

Contribute

Issues and pull requests are welcome to help this repository be the best it can be.

License

This repository falls under the MPL-2 license.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has 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
1.1.3 327 11/2/2023
1.1.2 213 10/14/2023
1.1.1 225 10/11/2023
1.1.0 221 10/5/2023
1.0.3 280 8/23/2023
1.0.2 303 8/22/2023
1.0.1 275 8/21/2023
1.0.0 296 8/9/2023