Created
September 29, 2018 03:23
-
-
Save tkokof/abd7a4c48a2105d82103afdf61aaf15b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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