2015-03-01 6 views
3

Я пытаюсь создать единицы измерения класса.Неловкий синтаксис общего метода с общим типом ограничений

То, что я до сих пор:

public abstract class UnitBase 
{ 
    protected double unitValue; 
    protected readonly double toSiFactor; 
    //more stuff goes here 
} 

//Meters, feet, miles etc will derive from this 
public class LengthUnit : UnitBase 
{ 
    //various methods and operator overloading 

    //Among others there is To<> for conversions, not really needed 
    //to understand the problem but relevant. 
    public L To<L>() where L : LengthUnit, new() 
    { 
     L converted = new L(); 
     converted.FromSi(ToSi()); //ToSi() and FromSi() are in UnitBase, omitted for brevity 
     return converted; 
    } 
} 

//Seconds Hours etc will derive from this 
public class TimeUnit : UnitBase 
{ 
    //Add() Subtract methods and various operator overloading 
} 

До сих пор так хорошо. Но теперь я хочу создавать сложные типы устройств, такие как скорость. Так вот:

public class SpeedUnit<S, T> : UnitBase 
    where S : LengthUnit, new() 
    where T : TimeUnit, new() 
{ 
    //======================= 
    //HERE IS THE AWKWARDNESS 
    //======================= 
    public U To<U, S1, T1>() 
     where U : SpeedUnit<S1, T1>, new() 
     where S1 : LengthUnit, new() 
     where T1 : TimeUnit, new() 
    { 
     U converted = new U(); 
     converted.FromSi(ToSi()); 
     return converted; 
    } 
} 

public class Knots : SpeedUnit<NauticalMiles, Hours> 
{ 
    //omitted code 
} 

public class FeetPerMinute : SpeedUnit<Feet, Minutes> 
{ 
    //omitted code 
} 

Так вот моя проблема: Предположим, что у вас есть Узлы и вы хотите, чтобы преобразовать их в FeetPerMinute. Что бы идеал:

Knots kts = new Knots(20); 
FeetPerMinute = kts.To<FeetPerMinute>(); 

Вместо этого я должен сделать:

FeetPerMinute = kts.To<FeetPerMinute, Feet, Minutes>(); 

Это немного неудобно и может получить событие хуже, когда дело доходит до еще более сложные типы, такие как силы. To() будет что-то вроде:

Newtons n = someForce.To<Newtons, Kilograms, Meters, Seconds>() 

или даже худший, если вы сделаете ошибку, чтобы использовать тип ускорения:

Newtons n = someForce.To<Newtons, Kilograms, Acceleration<Meters, Seconds>, Meters, Seconds>() 

Не очень удобно, особенно если у вас есть простота в виду. Итак, мои вопросы:

  • Есть ли способ сделать эту работу? (Помимо удаления общих параметров из SpeedUnit)
  • Почему не пресловутая вывод типа работы компилятора, чтобы определить, что Meters и Seconds уже есть в MetersPerSecond?
+0

Как насчет 'static From (U value)'? –

+0

@OndrejTucny Ах нет, я боюсь, что это не сработает: Каков тип возврата 'From'? 'U'? Или какой-то другой тип 'U1'? Не забывайте, что я хочу конвертировать из 'Knots' (' U') в 'FeetPerMinute' (' U1'). –

ответ

2

Короткий ответ заключается в том, что C# не позволяет выводить общие типы из других общих типов, только из параметров. Вы также можете сказать, что ограничения типа общего типа для этого не являются достаточно жесткими.

Однако, я действительно не понимаю, почему вы даже представляете разные единицы как разные классы? Мои личные советы состоят в том, чтобы использовать структуры для каждого физического измерения - не единицы. Таким образом, есть-структуру, как это:

public struct Length { 
    public static Length FromMeters(double meters) { 
     // ... 
    } 

    public double InMiles() { ... } 

    // operator overloads 
} 

Таким образом, у вас нет никаких проблем с дженериков, и вы даже не имеют влияния во время выполнения, но все еще есть время компиляции поддержки.

Предположим, у вас есть Length структур, Time и Speed, то вы можете легко перегружать операторы, которые позволяют разделить Length на более Time и получить Speed в качестве результата. Затем вы можете запросить значение скорости в любом устройстве, которое вам нравится, с помощью метода (скорости), например. InMilesPerSecond возвращение двойной.

Редактировать: Я предполагаю, что настоящая проблема в вашей оригинальной идее является проблемой неправильных абстракций. В частности, скорость не является (в смысле идентичности) длиной, деленной на время, хотя вы можете выразить ее таким образом. Это тонкая и тонкая разница. Например, вы можете выразить 1W = 1Nm или 1W = 1VA.Таким образом, вы не должны моделировать PowerType, Length>, потому что это не идентичность власти, а только способ ее вычисления.

+0

Я создал запрос на uservoice некоторое время назад для этого: http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3418197-make-it-possible-to-infer-generic-type- аргументы – Georg

+2

_However, я действительно не понимаю, почему вы даже представляете разные единицы как разные классы? _ - чтобы предотвратить смену будущих марсеров с помощью путаных миль, представленных как «двойные», с килограммами, представленными как те же самые «двойные» ? :-) –

+1

@OndrejTucny Есть намного лучшие способы спасти исследователей Марса, в частности решение, которое я только что предложил, за исключением того, что моя версия не имеет влияния во время работы, когда память является ценным и ограниченным ресурсом в Mars explorer. С помощью структур вы все равно можете перегружать операторы, ToMiles - это просто, когда вы в конце концов преобразуете конструкцию Length в double. – Georg