Click here to Skip to main content
14,573,349 members

Number.cs for general purpose Number handling in C#

Rate this:
5.00 (5 votes)
Please Sign up or sign in to vote.
5.00 (5 votes)
29 Jun 2020Ms-RL
An wrapper class for Number operations in C#
As C# does not offer a underlying base class for Numbers you have to write one that can work with all numbers without specifying the Type directly

Introduction

As I am working on my Project Morestachio ( https://github.com/JPVenson/morestachio ) i wanted to allow users to ether supply numbers from code but also allow number operations in my Template. That was where the trouble started and i needed to operate on two numbers that might not be the same or have unkown types. As i already had to write a parser to get the number from the text template i decided to allow all numbers to be wrapped into the Number.cs class and implemented all common operations on it.

Hint: The source on github contains references to the Morestachio Project within the MorestachioFormatter region. If you want to use only the Number.cs class you are save to delete this whole region. The Number.cs attached to this Tip does not contain this region!

Background

Number.cs is of interest for everyone who got numbers but without knowing what the number represents. Number.cs can parse any number from string according the rules of C# (https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-types and https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types#real-literals). 

Using the code

Number.cs has 3 different ways in how to create a new instace of Number.cs:

  1. bool Number.TryParse(string, CultureInfo, out Number).

    This tries to parse a string into a number based on the literal and the C# rules.

  2. Number.ctor(long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal)

    Use the constructor the create a new number with the internal .NET type

  3. implicit operator Number(Long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal)

    Number.cs implements the implicit casting operator for all .net Number types

You can get a list of all supported .NET types by calling Number.CsFrameworkIntegralTypes and Number.CsFrameworkFloatingPointNumberTypes Respectively.

Not that you got your instance of Number.cs you can make operations with it. There are two general ways in operating with Number.cs:

 1. Call the operation function like:

  • Add(Number)
  • Substract(Number)
  • Multiply(Number)
  • Divide(Number)
  • Modulo(Number)
  • ShiftLeft(Number)
  • ShiftRight(Number)
  • BiggerAs(Number)
  • SmallerAs(Number)
  • Equals(Number)
  • Same(Number)

Note: Equals(Number) will first check if both numbers are of the same .net type and then call Same(Number) for value equallity check
2. Use c# operators. Number.cs impliments the following operators on itself:

  • Number + Number
  • Number++
  • Number - Number
  • Number--
  • Number << Number
  • Number >> Number
  • Number == Number
  • Number != Number
  • Number < Number
  • Number > Number
  • Number <= Number
  •  Number >= Number

In addition to this operations on itself (Number + Number or Number < Number) you can also use the following operators on all other .net number types:

  • Number + long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal
  • Number - long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal
  • Number * long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal
  • Number / long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal
  • Number % long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal

These operators just call the methods from point 1.

Number.cs takes care of the implicit conversion of values from an operation. That means like in cs if you add two numbers together where one number is a floating point number the result will allways be a floating point number of the same type and if you add two integers together the result will always be the of the type that has more precision. This is done be a rating of numbers that exists within the Number.CsFrameworkFloatingPointNumberTypes and Number.CsFrameworkIntegralTypes. The function Number.GetOperationTargetType will return the type that takes prevalence over another. The operation then looks always the same like this:

Example Add Method:

/// <summary>
///        Adds the two numbers together
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public Number Add(Number other)
{
    var targetType = GetOperationTargetType(this, other);
    if (targetType == typeof(decimal))
    {
        return new Number(ToDecimal(null) + other.ToDecimal(null));
    }
    if (targetType == typeof(double))
    {
        return new Number(ToDouble(null) + other.ToDouble(null));
    }
    if (targetType == typeof(float))
    {
        return new Number(ToSingle(null) + other.ToSingle(null));
    }
    if (targetType == typeof(ulong))
    {
        return new Number(ToUInt64(null) + other.ToUInt64(null));
    }
    if (targetType == typeof(long))
    {
        return new Number(ToInt64(null) + other.ToInt64(null));
    }
    if (targetType == typeof(uint))
    {
        return new Number(ToUInt32(null) + other.ToUInt32(null));
    }
    if (targetType == typeof(int))
    {
        return new Number(ToInt32(null) + other.ToInt32(null));
    }
    if (targetType == typeof(ushort))
    {
        return new Number(ToUInt16(null) + other.ToUInt16(null));
    }
    if (targetType == typeof(short))
    {
        return new Number(ToInt16(null) + other.ToInt16(null));
    }
    if (targetType == typeof(byte))
    {
        return new Number(ToByte(null) + other.ToByte(null));
    }
    if (targetType == typeof(sbyte))
    {
        return new Number(ToSByte(null) + other.ToSByte(null));
    }
    throw new InvalidCastException($"Cannot convert {other.Value} ({other.Value.GetType()}) or {Value} ({Value.GetType()}) to a numeric type");
}

The same as over other number type the object Number.cs is implemented as a readonly struct and is inmutable. The code is tested and implemented for 

  • netstandard2.0
  • netcoreapp2.0 netcoreapp2.1 netcoreapp2.2 netcoreapp3.0
  • net46 net461 net462
  • net47 net471 net472

Points of Interest

I looked into a lot of code on github and everywhere else and was not able to find something like this general purpose so I decided to write it myself. I will keep devloping the number.cs class as i might want to support more functions like added support for Math. functions in the future so checkout the original file.

History

  • 28.06.2020 Init Commit

License

This article, along with any associated source code and files, is licensed under Microsoft Reciprocal License

Share

About the Author

GerVenson
Software Developer Freelancer
Germany Germany
I am a Young German Developer.

I was working since 2012 as a Junior Software Developer in the area of WPF with DevExpress and WinForms. I also had some experience with TSQL and Asp.net with AngularJS.

From January 2015 i will be working as an Software Consultant.

From June 2015 i will work as an Freelancer

Comments and Discussions

 
QuestionConsider to use Type.GetTypeCode() Pin
Christian Vogt29-Jun-20 11:14
professionalChristian Vogt29-Jun-20 11:14 
AnswerRe: Consider to use Type.GetTypeCode() Pin
GerVenson29-Jun-20 11:18
professionalGerVenson29-Jun-20 11:18 
GeneralRe: Consider to use Type.GetTypeCode() Pin
the Kris30-Jun-20 1:45
Memberthe Kris30-Jun-20 1:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Tip/Trick
Posted 29 Jun 2020

Stats

3.1K views
93 downloads
11 bookmarked