Skip to content

Instantly share code, notes, and snippets.

@tkokof
Created September 29, 2018 03:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tkokof/abd7a4c48a2105d82103afdf61aaf15b to your computer and use it in GitHub Desktop.
Save tkokof/abd7a4c48a2105d82103afdf61aaf15b to your computer and use it in GitHub Desktop.
// desc simple calculator constrains implementation
// note codes(adjusted) from https://www.codeproject.com/Articles/33617/Arithmetic-in-Generic-Classes-in-C
// maintainer hugoyu
using System;
/// <summary>
/// This interface defines all of the operations that can be done in generic classes
/// These operations can be assigned to operators in class Number<T>
/// </summary>
/// <typeparam name="T">Type that we will be doing arithmetic with</typeparam>
public interface ICalculator<T>
{
T Sum(T a, T b);
T Sub(T a, T b);
T Mul(T a, T b);
T Div(T a, T b);
int Compare(T a, T b);
//for doing integer division which is needed to do averages
// NOTE seems no need ...
//T Divide(T a, int b);
}
/// <summary>
/// ICalculator<T> implementation for Int32 type
/// </summary>
struct Int32Calculator : ICalculator<Int32>
{
public Int32 Sum(Int32 a, Int32 b)
{
return a + b;
}
public Int32 Sub(Int32 a, Int32 b)
{
return a - b;
}
public Int32 Mul(Int32 a, Int32 b)
{
return a * b;
}
public Int32 Div(Int32 a, Int32 b)
{
return a / b;
}
public int Compare(Int32 a, Int32 b)
{
var diff = Sub(a, b);
if (diff < 0)
{
return -1;
}
else if (diff > 0)
{
return 1;
}
return 0;
}
}
/// <summary>
/// ICalculator<T> implementation for Int64 type
/// </summary>
struct Int64Calculator : ICalculator<Int64>
{
public Int64 Sum(Int64 a, Int64 b)
{
return a + b;
}
public Int64 Sub(Int64 a, Int64 b)
{
return a - b;
}
public Int64 Mul(Int64 a, Int64 b)
{
return a * b;
}
public Int64 Div(Int64 a, Int64 b)
{
return a / b;
}
public int Compare(Int64 a, Int64 b)
{
var diff = Sub(a, b);
if (diff < 0)
{
return -1;
}
else if (diff > 0)
{
return 1;
}
return 0;
}
}
/// <summary>
/// ICalculator<T> implementation for Single type
/// </summary>
struct SingleCalculator : ICalculator<Single>
{
public Single Sum(Single a, Single b)
{
return a + b;
}
public Single Sub(Single a, Single b)
{
return a - b;
}
public Single Mul(Single a, Single b)
{
return a * b;
}
public Single Div(Single a, Single b)
{
return a / b;
}
public int Compare(Single a, Single b)
{
var diff = Sub(a, b);
if (diff < 0)
{
return -1;
}
else if (diff > 0)
{
return 1;
}
return 0;
}
}
/// <summary>
/// ICalculator<T> implementation for Double type
/// </summary>
struct DoubleCalculator : ICalculator<Double>
{
public Double Sum(Double a, Double b)
{
return a + b;
}
public Double Sub(Double a, Double b)
{
return a - b;
}
public Double Mul(Double a, Double b)
{
return a * b;
}
public Double Div(Double a, Double b)
{
return a / b;
}
public int Compare(Double a, Double b)
{
var diff = Sub(a, b);
if (diff < 0)
{
return -1;
}
else if (diff > 0)
{
return 1;
}
return 0;
}
}
/// <summary>
/// This class uses reflection to automatically create the correct
/// ICalculator<T> that is needed for any particular type T.
/// </summary>
/// <typeparam name="T">Type that we will be doing arithmetic with</typeparam>
public class Number<T>
{
/// <summary>
/// default value placeholder
/// </summary>
static Number<T> defaultNumber = new Number<T>(default(T));
public static Number<T> Default { get { return defaultNumber; } }
private T value;
public Number(T value)
{
this.value = value;
}
/// <summary>
/// Big IF chain to decide exactly which ICalculator needs to be created
/// Since the ICalculator is cached, this if chain is executed only once per type
/// </summary>
/// <returns>The type of the calculator that needs to be created</returns>
public static Type GetCalculatorType()
{
Type tType = typeof(T);
Type calculatorType = null;
if (tType == typeof(Int32))
{
calculatorType = typeof(Int32Calculator);
}
else if (tType == typeof(Int64))
{
calculatorType = typeof(Int64Calculator);
}
else if (tType == typeof(Single))
{
calculatorType = typeof(SingleCalculator);
}
else if (tType == typeof(Double))
{
calculatorType = typeof(DoubleCalculator);
}
else
{
throw new InvalidCastException(String.Format("Unsupported Type- Type {0}" +
" does not have a partner implementation of interface " +
"ICalculator<T> and cannot be used in generic " +
"arithmetic using type Number<T>", tType.Name));
}
return calculatorType;
}
/// <summary>
/// a static field to store the calculator after it is created
/// this is the caching that is refered to above
/// </summary>
private static ICalculator<T> fCalculator = null;
/// <summary>
/// Singleton pattern- only one calculator created per type
/// </summary>
public static ICalculator<T> Calculator
{
get
{
if (fCalculator == null)
{
MakeCalculator();
}
return fCalculator;
}
}
/// <summary>
/// Here the actual calculator is created using the system activator
/// </summary>
public static void MakeCalculator()
{
Type calculatorType = GetCalculatorType();
fCalculator = Activator.CreateInstance(calculatorType) as ICalculator<T>;
}
/// These methods can be called by the applications
/// programmer if no operator overload is defined
/// If an operator overload is defined these methods are not needed
#region operation methods
public static T Sum(T a, T b)
{
return Calculator.Sum(a, b);
}
public static T Sub(T a, T b)
{
return Calculator.Sub(a, b);
}
public static T Mul(T a, T b)
{
return Calculator.Mul(a, b);
}
public static T Div(T a, T b)
{
return Calculator.Div(a, b);
}
public static int Compare(T a, T b)
{
return Calculator.Compare(a, b);
}
#endregion
/// These operator overloads make doing the arithmetic easy.
/// For custom operations, an operation method
/// may be the only way to perform the operation
#region Operators
//IMPORTANT: The implicit operators
//allows an object of type Number<T> to be
//easily and seamlessly wrap an object of type T.
public static implicit operator Number<T>(T a)
{
return new Number<T>(a);
}
//IMPORTANT: The implicit operators allows
//an object of type Number<T> to be
//easily and seamlessly wrap an object of type T.
public static implicit operator T(Number<T> a)
{
return a.value;
}
public static Number<T> operator +(Number<T> a, Number<T> b)
{
return Calculator.Sum(a.value, b.value);
}
public static Number<T> operator -(Number<T> a, Number<T> b)
{
return Calculator.Sub(a, b);
}
public static Number<T> operator *(Number<T> a, Number<T> b)
{
return Calculator.Mul(a, b);
}
public static Number<T> operator /(Number<T> a, Number<T> b)
{
return Calculator.Div(a, b);
}
public static bool operator >(Number<T> a, Number<T> b)
{
return Calculator.Compare(a, b) > 0;
}
public static bool operator <(Number<T> a, Number<T> b)
{
return Calculator.Compare(a, b) < 0;
}
#endregion
/// <summary>
/// overrides ToString() to value's ToString()
/// </summary>
public override string ToString()
{
return value.ToString();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment