Writing General Arithmetic in C #

I have a list of numbers, and I wrote a method that does some calculations on these numbers; overall, this is about a code page. The method does some arithmetic and compares these numbers.

My problem is that in one case the list is IList<byte> and in the other case IList<float> . The algorithm in both cases is exactly the same (yes, I know things like overflow errors and loss of precision, but in my case it works). How to write a method that will process both lists? I cannot write something like void DoStuff<T>(IList<T> numbers) , because there are no arithmetic operators ( + - * / ) that are common.

One solution is to simply store everything as a float, but I would like to avoid it. The lists are quite long, and therefore saving a float instead of bytes will cost too much memory. I could also do something like DoStuffFloat(byteList.Select(b => (float)b)) , but I don't want to pay a performance penalty if I can avoid it.

Copying the whole method and replacing the "float" with "byte" (or vice versa) is ending, is there a decent solution?

EDIT: I should have mentioned that I have limited access to .NET 3.5 for this project.

+4
source share
3 answers

I do not know if this is the best method for your case, but it is also useful for such cases.

This can be done using the dynamic keyword. What kind of dynamics will do, it will not check compile time before runtime.

Here is a small sample program showing how it works.

 class Program { static void Main() { List<byte> bytes = new List<byte>(); bytes.Add(2); bytes.Add(1); List<float> floats = new List<float>(); floats.Add(2.5F); floats.Add(1F); Console.WriteLine(DoStuff(bytes)); Console.WriteLine(DoStuff(floats)); Console.ReadLine(); } static dynamic DoStuff(IList items) { dynamic item0 = items[0]; dynamic item1 = items[1]; return item0 - item1; } } 

Unfortunately, in my quick testing, I could not do IList<dynamic> work , but using a non-generic IList , then accessing elements like dynamic works fine.

+5
source

What you can do is create a common interface that includes the operations you want to support, create a common factory to create instances of supported types to perform operations and use them.

eg.

 public interface IOperations<T> { T Add(T a, T b); T Subtract(T a, T b); T Multiply(T a, T b); T Divide(T a, T b); } public static class Operations<T> { public static IOperations<T> Default { get { return Create(); } } static IOperations<T> Create() { var type = typeof(T); switch (Type.GetTypeCode(type)) { case TypeCode.Byte: return (IOperations<T>)new ByteOperations(); case TypeCode.Single: return (IOperations<T>)new SingleOperations(); default: var message = String.Format("Operations for type {0} is not supported.", type.Name); throw new NotSupportedException(message); } } class ByteOperations : IOperations<byte> { public byte Add(byte a, byte b) { return unchecked ((byte)(a + b)); } public byte Subtract(byte a, byte b) { return unchecked ((byte)(a - b)); } public byte Multiply(byte a, byte b) { return unchecked ((byte)(a * b)); } public byte Divide(byte a, byte b) { return unchecked ((byte)(a / b)); } } class SingleOperations : IOperations<float> { public float Add(float a, float b) { return a + b; } public float Subtract(float a, float b) { return a - b; } public float Multiply(float a, float b) { return a * b; } public float Divide(float a, float b) { return a / b; } } } 
 T Mean<T>(IList<T> numbers) { var operations = Operations<T>.Default; var sum = numbers.Aggregate(operations.Add); var count = (T)Convert.ChangeType(numbers.Count, typeof(T)); return operations.Divide(sum, count); } var resultByte = Mean(new byte[] { 1, 2, 3, 4 }); // 2 var resultSingle = Mean(new float[] { 1.1F, 2.1F, 3.1F, 4.1F }); // 2.6F var resultInt = Mean(new int[] { 1, 2, 3, 4 }); // not supported 

If you are not opposed to a small performance hit, you can dynamically create the necessary operations.

 class GenericOperations<T> : IOperations<T> { public GenericOperations() { add = CreateLambda(Expression.Add); subtract = CreateLambda(Expression.Subtract); multiply = CreateLambda(Expression.Multiply); divide = CreateLambda(Expression.Divide); } private Func<T, T, T> add, subtract, multiply, divide; private static Func<T, T, T> CreateLambda(Func<Expression, Expression, BinaryExpression> op) { var a = Expression.Parameter(typeof(T), "a"); var b = Expression.Parameter(typeof(T), "b"); var body = op(a, b); var expr = Expression.Lambda<Func<T, T, T>>(body, a, b); return expr.Compile(); } public T Add(T a, T b) { return add(a, b); } public T Subtract(T a, T b) { return subtract(a, b); } public T Multiply(T a, T b) { return multiply(a, b); } public T Divide(T a, T b) { return divide(a, b); } } 
+4
source

Create classes to wrap basic values, and each of them implements an interface with the necessary operations. Then use the IList this interface instead of the raw values.

0
source

Source: https://habr.com/ru/post/1434084/


All Articles