Арифметические действия в обобщенных классах - C#
Формулировка задачи:
Здравствуйте! Нужен пример, как ограничить обобщенный тип для написания универсального класса так, чтобы можно было выполнять действия сложения, вычитания, деления и т. п.
P.S. Нужен именно пример кода. На фразы, типа:"Ограничь тип структурой или интерфейсом, а действия реализуй при помощи делегатов" натыкаюсь постоянно, но не совсем понимаю, как это все реализовывается на практике.
Заранее спасибо.
Решение задачи: «Арифметические действия в обобщенных классах»
textual
Листинг программы
static class MathEx
{
}
static class MathEx<T>
{
public static readonly Func<T, T, T> add = BuildBinaryOperator(ExpressionType.Add);
public static readonly Func<T, T, T> sub = BuildBinaryOperator(ExpressionType.Subtract);
public static readonly Func<T, T, T> mul = BuildBinaryOperator(ExpressionType.Multiply);
public static readonly Func<T, T, T> div = BuildBinaryOperator(ExpressionType.Divide);
static Func<T,T,T> BuildBinaryOperator(ExpressionType Type)
{
var t = typeof(T);
var a = Expression.Parameter(typeof(T), "a");
var b = Expression.Parameter(typeof(T), "b");
var o = Expression.MakeBinary(Type, a, b);
return Expression.Lambda<Func<T, T, T>>(o, a, b).Compile();
}
}
abstract class Vector
{
public static Vector<T> Create<T>(params T[] Components)
=> new Vector<T>(Components);
}
class Vector<T>: Vector
{
T[] data;
public Vector(int Length)
{
data = new T[Length];
}
public Vector(IEnumerable<T> Components)
{
data = Components.ToArray();
}
public int Length => data.Length;
public T this[int Index]
{
get => data[Index];
set => data[Index] = value;
}
public override string ToString()
{
return $"<{ string.Join("; ", data) }>";
}
private static IEnumerable<R> CheckedZip<R>(Vector<T> Left, Vector<T> Right, Func<T, T, R> ResultSelector)
{
if (Left.Length != Right.Length)
throw new ArgumentException("Vectors must be the same lengths");
return Left.data.Zip(Right.data, ResultSelector);
}
public static Vector<T> operator +(Vector<T> Left, Vector<T> Right)
=> new Vector<T>(CheckedZip(Left, Right, MathEx<T>.add));
public static Vector<T> operator -(Vector<T> Left, Vector<T> Right)
=> new Vector<T>(CheckedZip(Left, Right, MathEx<T>.sub));
public static T operator *(Vector<T> Left, Vector<T> Right)
=> CheckedZip(Left, Right, MathEx<T>.mul).Aggregate(MathEx<T>.add);
public static Vector<T> operator *(Vector<T> Vector, T Scalar)
=> new Vector<T>(Vector.data.Select(Component => MathEx<T>.mul(Component, Scalar)));
public static Vector<T> operator *(T Scalar, Vector<T> Vector)
=> new Vector<T>(Vector.data.Select(Component => MathEx<T>.mul(Scalar, Component)));
public static Vector<T> operator /(Vector<T> Vector, T Scalar)
=> new Vector<T>(Vector.data.Select(Component => MathEx<T>.div(Component, Scalar)));
}
struct MyValue
{
public readonly string Value;
public MyValue(string Value) => this.Value = Value;
public override string ToString() => Value;
public static MyValue operator +(MyValue left, MyValue right) => new MyValue(left.Value + right.Value);
public static MyValue operator -(MyValue left, MyValue right) => throw new NotImplementedException();
public static MyValue operator *(MyValue left, MyValue right) => throw new NotImplementedException();
public static MyValue operator /(MyValue left, MyValue right) => throw new NotImplementedException();
public static explicit operator MyValue(string Value) => new MyValue(Value);
}
class Program
{
static void TryAdd<T>(Vector<T> left, Vector<T> right)
{
Console.WriteLine($"{typeof(T).Name, 10}: {left} + {right} = {left + right}");
}
static void Main()
{
var s1 = Vector.Create((MyValue)"a", (MyValue)"b");
var s2 = Vector.Create((MyValue)"A", (MyValue)"B");
TryAdd(s1, s2);
var i1 = Vector.Create(1, 2);
var i2 = Vector.Create(3, 4);
TryAdd(i1, i2);
var f1 = Vector.Create(1.2f, 3.4f);
var f2 = Vector.Create(5.6f, 7.8f);
TryAdd(f1, f2);
var d1 = Vector.Create(1.2d, 3.4d);
var d2 = Vector.Create(5.6d, 7.8d);
TryAdd(d1, d2);
Console.ReadLine();
}
}