0

This is what I have as an "Add" function in my Maths library:

public static int Add(int a, int b) => a + b;

public static float Add(float a, float b) => a + b;

public static int Add(int a, int b, int c) => a + b + c;

public static float Add(float a, float b, float c) => a + b + c;

public static int Add(int a, int b, int c, int d) => a + b + c + d;

public static float Add(float a, float b, float c, float d) => a + b + c + d;

public static int Add(List<int> numbers)
{
    int result = 0;

    foreach (int n in numbers) result += n;

    return result;
}

public static float Add(List<float> numbers)
{
    float result = 0;

    foreach (float n in numbers) result += n;

    return result;
}

public static int Add(int[] numbers)
{
    int result = 0;

    foreach (int n in numbers) result += n;

    return result;
}

public static float Add(float[] numbers)
{
    float result = 0;

    foreach (float n in numbers) result += n;

    return result;
}

Is there any way to achieve the same result (having a function that works both with int and float and with different parameters) with fewer variations of this function (just to make the code easier to understand)?

EDIT: I'm working with Unity 2022.1.7f1, so for everyone suggesting to use generic math, I don't believe Unity supports C# 11 yet. Please correct me if I'm wrong.

  • I might suggest using a params argument: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/params – Jakob Lovern Aug 12 '22 at 17:16
  • If you want to cover all collections, just do `IEnumerable` and `IEnumerable` instead of a list and an array. If you want the user to be able to pass an array as well as numbers as parameters, use the `params` keyword with an array. These two functions for each float and int should cover most (if not all) cases. – Jesse Aug 12 '22 at 17:16
  • It looks like your code bodies for `int` and `float` are identical too. You might get some benefit from using a generic method instead: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generic-methods – Jakob Lovern Aug 12 '22 at 17:19
  • Maybe you can define a generic method: https://stackoverflow.com/questions/14541676/can-i-create-a-generic-method-for-sum-in-c-sharp – aminrd Aug 12 '22 at 17:28
  • I wasn't able to get it to work using generics. Could show me what the function would look like? – Elia Giaccardi Old Aug 12 '22 at 17:33
  • @JakobLovern trolling? You hopefully know that there is no way to restrict generics to numeric types nor have +/-/*... to be used on generic parameters. So suggesting to use generics looks like an attempt to waste OP's time and generally cause frustration. It is plausible that some sort of support will be there in the future - https://github.com/dotnet/designs/pull/257 but at this point https://jonskeet.uk/csharp/miscutil/usage/genericoperators.html is probably the best OP can get... – Alexei Levenkov Aug 12 '22 at 17:38
  • @EliaGiaccardi ohhh... you already wasted time on trying to make it work with generics so Jakob was late with the (not so nice) suggestion... sorry about the wasted effor - but really appreciate that you tried before asking! – Alexei Levenkov Aug 12 '22 at 17:39
  • I mean technically you could simply just create an Add method that adds any two objects of the same type as long as they implement + and =. I legitimately didn't realize that C# numeric types couldn't be derived from a generic interface, and only realized that after a few minutes of trying it myself – Jakob Lovern Aug 12 '22 at 17:45
  • (I totally missed that "the future" is here - .Net 7 Preview has the generic math https://devblogs.microsoft.com/dotnet/dotnet-7-generic-math/, sorry for living in *the past*) – Alexei Levenkov Aug 12 '22 at 17:49
  • @JakobLovern _as long as they implement + and = _ the problem is that in .Net (up to Net.6) you cannot use a generic with a constraint such `where T: hasOperator+, hasOperator+=` Possibly, you are thinking at C++ templates, which are another beast. – Gian Paolo Aug 12 '22 at 19:23

2 Answers2

3

I was able to bring it down from 10 to 4 variations using some recommendations from the comments.

public static int Add(params int[] numbers)
{
    return Add(numbers);
    //Creates array and calls the next function
}

public static int Add(IEnumerable<int> numbers)
{
    int result = 0;

    foreach (int n in numbers) result += n;

    return result;
}


public static float Add(params float[] numbers)
{
    return Add(numbers);
    //Creates array and calls the next function
} 

public static float Add(IEnumerable<float> numbers)
{
    float result = 0;

    foreach (float n in numbers) result += n;

    return result;
}

If you have any more tips please feel free to share them.

EDIT: I'm working with Unity 2022.1.7f1, so for everyone suggesting to use generic math, I don't believe Unity supports C# 11 yet. Please correct me if I'm wrong.

UPDATE: I tried changing IEnumerable<> to List<> and it gives me no errors even if the parameter is an array and not a list, I have not yet tested it's functionality though.

UPDATE: After testing, it turns out that the first function simply calls itself. I fixed it like this;

public static int Sum(params int[] numbers)
{
    List<int> numbersList = new List<int>();
    numbersList.AddRange(numbers);

    return Sum(numbersList);
}

public static int Sum(List<int> numbers)
{
    int result = 0;

    foreach (int n in numbers) result += n;

    return result;
}


public static float Sum(params float[] numbers)
{
    List<float> numbersList = new List<float>();
    numbersList.AddRange(numbers);

    return Sum(numbersList);
}

public static float Sum(List<float> numbers)
{
    float result = 0;

    foreach (float n in numbers) result += n;

    return result;
}

Also, this makes it so that it is usable with both arrays and lists without the need to use IEnumerables, which means that it can be used in other functions like Min where indexing is necessary.

  • 2
    You could use the new "[Generic Math](https://devblogs.microsoft.com/dotnet/dotnet-7-generic-math/)" feature of .NET 7. – Uwe Keim Aug 12 '22 at 17:41
  • 1
    Note that for + you can simply use `Sum` but that does not translate well to other operators - so `foreach` is fine for consistency if you plan to implement the rest (*, -, /, ...) (Unless you can go with preview of Net7 as @UweKeim suggested) – Alexei Levenkov Aug 12 '22 at 17:42
1

C# 11 fixes precisely this problem with INumber<T> interface.

https://youtu.be/1K44Nu9_7U8?t=19

Edit: Adding an example with generics since Unity engine might not support C# 11 yet.

namespace GenericCalculations
{
    public class GenericMath
    {
        public static object Add<T>(List<T> listOfT)
        {
            if (typeof(T) == typeof(int))
            {
                return listOfT.Sum(t => t as int?);
            }
            if (typeof(T) == typeof(double))
            {
                return listOfT.Sum(t => t as double?);
            }
            // etc 
            throw new ArgumentException("Invalid entry");
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
            Console.WriteLine(GenericMath.Add(new List<double>() { 1.1, 1.2, 1.3 }));
            Console.WriteLine(GenericMath.Add(new List<int>() { 1, 1, 1 }));

        }
    }
}
Mayur Ekbote
  • 1,700
  • 1
  • 11
  • 14