0

Context:

I am implementing a BigRational struct in C# which requires various arithmetic and comparison overloads. The code in many of the overloads look exactly the same because of the use of var. On top of this I am getting a warning for CA2225 which states that various operators need a "friendly alternatively named" method. See https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2225

Since many arithmetic operators are overloaded for

  • (BigRational, BigRational) -> BigRational
  • (BigRational, BigInteger) -> BigRational
  • (BigInteger, BigRational) -> BigRational
  • (BigRational, long) -> BigRational
  • (long, BigRational) -> BigRational
  • (BigRational, ulong) -> BigRational
  • (ulong, BigRational) -> BigRational

and comparison operators for

  • (BigRational, BigRational) -> bool
  • (BigRational, BigInteger) -> bool
  • (BigInteger, BigRational) -> bool
  • (BigRational, long) -> bool
  • (long, BigRational) -> bool
  • (BigRational, ulong) -> bool
  • (ulong, BigRational) -> bool

this results in a significant amount of duplicate code and boilerplate.

Question:

Is there a simple way to use C# 9 style generators to implement this?

Side Thought:

It would be great if C# could get overload generation built into the language so that

public static BigRational Add<T>(BigRational augend, T addend)
    where T overloads: BigInteger, long, ulong
{
    // use var in code...
}

is equivalent to

public static BigRational Add(BigRational augend, BigInteger addend)
{
}

public static BigRational Add(BigRational augend, long addend)
{
}

public static BigRational Add(BigRational augend, ulong addend)
{
}

This would also allow one to write a single function for types that do not share a base class or interface, but have very similar APIs.

Quantifeye
  • 261
  • 2
  • 10
  • Why more simple approach of implicit conversion to your type did not work? – Alexei Levenkov Dec 12 '20 at 23:57
  • Such refactoring tool is not available in Visual Studio but perhaps a plugin like ReSharper can do that else you can create your own. –  Dec 13 '20 at 04:24
  • What you are asking for looks more like C++ templates than .NET generics, isn't it? [C# generics compared to C++ templates](https://stackoverflow.com/questions/1208153/c-sharp-generics-compared-to-c-templates) & [What are the differences between Generics in C# and Java… and Templates in C++?](https://stackoverflow.com/questions/31693/what-are-the-differences-between-generics-in-c-sharp-and-java-and-templates-i) & [Differences Between C++ Templates and C# Generics](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/differences-between-cpp-templates-and-csharp-generics]) –  Dec 13 '20 at 04:24

1 Answers1

0

Here's an idea for a source generator you could write, which would allow you to solve this problem in general. I'm not going to write the source generator, as that would be quite a bit of work, but I'll highlight a design for it.

You would write your class like this:

public partial struct BigRational
{
    public static BigRational Add(BigRational augend, [GenerateOverloadsFor(typeof(long), typeof(ulong ))] BigInteger addend)
    {
    }
}

Then the source generator would detect usages of GenerateOverloadsForAttribute and generate the following:

partial struct BigRational
{
    public static BigRational Add(BigRational augend, long addend)
    {
    }

    public static BigRational Add(BigRational augend, ulong addend)
    {
    }
}

Whilst it's unlikely to be worth writing a source generator for this specific case, this solution should hopefully be generic enough to work for other use cases as well.

To keep your source generator straightforward it would probably just copy the code straight over from the 'template' to the generated overloads. This means that you'll have to write your code carefully so that it works for all overloads, or else you'll get compile time errors.

Yair Halberstadt
  • 5,733
  • 28
  • 60