Здравствуйте, Cyberax, Вы писали:
C>Не ортогонально. Оно у тебя просто прозрачно боксирует value-типы — и "возвращаемся обратно на клетку 1", получая весь оверхед, связаный с объектами.
Вообще-то это не так.
Вот доказательство:
Как сделать на c# обобщенную арифметику? Это достаточно просто, хотя и выглядит весьма уродливо. Но работает.
Можно построить целую иерархию "классов типов":
public interface CComparable<T>
{
bool GT(T a, T b);
bool LT(T a, T b);
bool Eq(T a, T b, T eps);
}
public interface CEq<T>
{
bool Eq(T a, T b);
bool NonEq(T a, T b);
}
public interface COrdered<T> : CComparable<T>, CEq<T>
{
bool GTorEq(T a, T b);
bool LTorEq(T a, T b);
}
public interface CNumber<T> : CComparable<T>
{
T Add(T a, T b);
T Sub(T a, T b);
T AddNeutral { get;} // 0
T Prod(T a, T b);
T ProdNeutral { get;} // 1
T Abs(T a);
T Negate(T a);
T Signum(T a);
}
public interface CFractional<T> : CNumber<T>
{
T From(double a);
T From(float a);
T Quot(T a, T b);
T Inv(T a);
}
public interface CInteger<T> : CNumber<T>, COrdered<T>
{
T From(int a);
T From(long a);
T Div(T a, T b);
T Mod(T a, T b);
}
public interface CReal<T> : CFractional<T>
{
T Sqr(T a);
T Sqrt(T a);
}
А теперь, написать имплементации, например RealDouble:
public struct RealDouble : CReal<double> // обратите внимание на struct
{
#region CFractional<double> Members
public double From(double a)
{
return a;
}
public double From(float a)
{
return a;
}
public double Quot(double a, double b)
{
return a / b;
}
public double Inv(double a)
{
return 1 / a;
}
#endregion
#region CNumber<double> Members
public double Add(double a, double b)
{
return a + b;
}
public double Sub(double a, double b)
{
return a - b;
}
public double AddNeutral
{
get { return 0.0; }
}
public double Prod(double a, double b)
{
return a * b;
}
public double ProdNeutral
{
get { return 1.0; }
}
public double Abs(double a)
{
return Math.Abs(a);
}
public double Negate(double a)
{
return -a;
}
public double Signum(double a)
{
return Math.Sign(a);
}
#endregion
#region CComparable<double> Members
public bool GT(double a, double b)
{
return a > b;
}
public bool LT(double a, double b)
{
return a < b;
}
public bool Eq(double a, double b, double eps)
{
return Math.Abs(a - b) < eps;
}
#endregion
#region CReal<double> Members
public double Sqr(double a)
{
return a * a;
}
public double Sqrt(double a)
{
return Math.Sqrt(a);
}
#endregion
}
Теперь сделаем комплексные числа:
public struct Complex
{
public Complex(double re, double im)
{
Re = re;
Im = im;
}
public double Re;
public double Im;
public static Complex operator +(Complex a, Complex b)
{
return new Complex(a.Re + b.Re, a.Im + b.Im);
}
}
public struct Complex<T, C>
where C : struct, CReal<T>
{
static C c = new C();
public Complex(T re, T im)
{
Re = re;
Im = im;
}
public T Re;
public T Im;
public static Complex<T, C> operator +(Complex<T, C> a, Complex<T, C> b)
{
return new Complex<T, C>(c.Add(a.Re, b.Re), c.Add(a.Im, b.Im));
}
}
Идея ясна? Если правы Вы, то во втором случае будут виртуальные вызовы (даже хуже, потому что CReal<T> это интерфейс)
Я же утверждаю, что никаких виртуальных вызовов не будет, более того, функция будет заинлайнена JIT.
Теперь пишем вот такой вот тест:
class Program
{
static void Main(string[] args)
{
System.Threading.Thread.Sleep(2000); // дадим время рантайму.
Stopwatch sw = new Stopwatch();
int count = 100000000;
Complex c1 = new Complex();
Complex c2 = new Complex(0, 1);
Complex<double, RealDouble> gc1 = new Complex<double, RealDouble>();
Complex<double, RealDouble> gc2 = new Complex<double, RealDouble>(0, 1);
for (int i = 0; i < count/1000; i++) // это для того, чтобы исключить из замеров JIT-компиляцию.
{
c2 = c2 + c1;
}
for (int i = 0; i < count/1000; i++)
{
gc2 = gc2 + gc1;
}
sw.Start();
for (int i = 0; i < count; i++)
{
c2 = c2 + c1;
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < count; i++)
{
gc2 = gc2 + gc1;
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
Console.ReadLine();
}
}
Компилируем в Release, запускаем без отладчика.
.NET Framework 2.0.50727
Xeon-A(Prestonia) X2, 2666 MHz
Получаем вот что:
2457
2454
quod erat demonstrandum.
... << RSDN@Home 1.2.0 alpha rev. 655>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll