Jak utworzyć ogólny konwerter jednostek miar w C#?

Próbowałem dowiedzieć się trochę więcej o delegatach i lambdach podczas pracy nad małym projektem gotowania, który obejmuje konwersję temperatury, a także niektóre konwersje pomiaru gotowania, takie jak Imperialne na metryczne i próbowałem wymyślić sposób, aby rozszerzyć konwerter jednostek.

Oto, od czego zacząłem, wraz z komentarzami kodu na temat niektórych moich planów. Nie mam zamiaru go używać jak poniżej, testowałem tylko niektóre funkcje C# Nie wiem bardzo dobrze, nie jestem również pewien, jak posunąć się dalej. Czy ktoś ma jakieś sugestie jak stworzyć to o czym mówię w komentarzach poniżej? Dzięki

namespace TemperatureConverter
{
    class Program
    {
        static void Main(string[] args)
        {
            // Fahrenheit to Celsius :  [°C] = ([°F] − 32) × 5⁄9
            var CelsiusResult = Converter.Convert(11M,Converter.FahrenheitToCelsius);

            // Celsius to Fahrenheit : [°F] = [°C] × 9⁄5 + 32
            var FahrenheitResult = Converter.Convert(11M, Converter.CelsiusToFahrenheit);

            Console.WriteLine("Fahrenheit to Celsius : " + CelsiusResult);
            Console.WriteLine("Celsius to Fahrenheit : " + FahrenheitResult);
            Console.ReadLine();

            // If I wanted to add another unit of temperature i.e. Kelvin 
            // then I would need calculations for Kelvin to Celsius, Celsius to Kelvin, Kelvin to Fahrenheit, Fahrenheit to Kelvin
            // Celsius to Kelvin : [K] = [°C] + 273.15
            // Kelvin to Celsius : [°C] = [K] − 273.15
            // Fahrenheit to Kelvin : [K] = ([°F] + 459.67) × 5⁄9
            // Kelvin to Fahrenheit : [°F] = [K] × 9⁄5 − 459.67
            // The plan is to have the converters with a single purpose to convert to
            //one particular unit type e.g. Celsius and create separate unit converters 
            //that contain a list of calculations that take one specified unit type and then convert to their particular unit type, in this example its Celsius.
        }
    }

    // at the moment this is a static class but I am looking to turn this into an interface or abstract class
    // so that whatever implements this interface would be supplied with a list of generic deligate conversions
    // that it can invoke and you can extend by adding more when required.
    public static class Converter
    {
        public static Func<decimal, decimal> CelsiusToFahrenheit = x => (x * (9M / 5M)) + 32M;
        public static Func<decimal, decimal> FahrenheitToCelsius = x => (x - 32M) * (5M / 9M);

        public static decimal Convert(decimal valueToConvert, Func<decimal, decimal> conversion) {
            return conversion.Invoke(valueToConvert);
        }
    }
}

Update: próbuje wyjaśnić moje pytanie:

Używając poniższego przykładu temperatury, jak utworzyć klasę, która zawiera listę konwersji lambda do Celsjusza, którą następnie przekażesz podaną temperaturę i spróbuje ją przekonwertować na Celsjusza (jeśli obliczenia są dostępne)

Przykład pseudo kod:

enum Temperature
{
    Celcius,
    Fahrenheit,
    Kelvin
}

UnitConverter CelsiusConverter = new UnitConverter(Temperature.Celsius);
CelsiusConverter.AddCalc("FahrenheitToCelsius", lambda here);
CelsiusConverter.Convert(Temperature.Fahrenheit, 11);
Author: Danny Tuppeny, 2011-10-21

6 answers

Pomyślałem, że to interesujący mały problem, więc postanowiłem zobaczyć, jak ładnie można to ująć w ogólną implementację. Nie jest to dobrze przetestowane( i nie obsługuje wszystkich przypadków błędów - na przykład jeśli nie zarejestrujesz konwersji dla określonego typu jednostki, a następnie przekaż ją), ale może to być przydatne. Skupiono się na uczynieniu dziedziczonej klasy (TemperatureConverter) jak najbardziej uporządkowanym.

/// <summary>
/// Generic conversion class for converting between values of different units.
/// </summary>
/// <typeparam name="TUnitType">The type representing the unit type (eg. enum)</typeparam>
/// <typeparam name="TValueType">The type of value for this unit (float, decimal, int, etc.)</typeparam>
abstract class UnitConverter<TUnitType, TValueType>
{
    /// <summary>
    /// The base unit, which all calculations will be expressed in terms of.
    /// </summary>
    protected static TUnitType BaseUnit;

    /// <summary>
    /// Dictionary of functions to convert from the base unit type into a specific type.
    /// </summary>
    static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsTo = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();

    /// <summary>
    /// Dictionary of functions to convert from the specified type into the base unit type.
    /// </summary>
    static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsFrom = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();

    /// <summary>
    /// Converts a value from one unit type to another.
    /// </summary>
    /// <param name="value">The value to convert.</param>
    /// <param name="from">The unit type the provided value is in.</param>
    /// <param name="to">The unit type to convert the value to.</param>
    /// <returns>The converted value.</returns>
    public TValueType Convert(TValueType value, TUnitType from, TUnitType to)
    {
        // If both From/To are the same, don't do any work.
        if (from.Equals(to))
            return value;

        // Convert into the base unit, if required.
        var valueInBaseUnit = from.Equals(BaseUnit)
                                ? value
                                : ConversionsFrom[from](value);

        // Convert from the base unit into the requested unit, if required
        var valueInRequiredUnit = to.Equals(BaseUnit)
                                ? valueInBaseUnit
                                : ConversionsTo[to](valueInBaseUnit);

        return valueInRequiredUnit;
    }

    /// <summary>
    /// Registers functions for converting to/from a unit.
    /// </summary>
    /// <param name="convertToUnit">The type of unit to convert to/from, from the base unit.</param>
    /// <param name="conversionTo">A function to convert from the base unit.</param>
    /// <param name="conversionFrom">A function to convert to the base unit.</param>
    protected static void RegisterConversion(TUnitType convertToUnit, Func<TValueType, TValueType> conversionTo, Func<TValueType, TValueType> conversionFrom)
    {
        if (!ConversionsTo.TryAdd(convertToUnit, conversionTo))
            throw new ArgumentException("Already exists", "convertToUnit");
        if (!ConversionsFrom.TryAdd(convertToUnit, conversionFrom))
            throw new ArgumentException("Already exists", "convertToUnit");
    }
}

Typ generyczny args jest dla enum, które reprezentuje jednostki, a typ dla wartość. Aby go użyć, wystarczy dziedziczyć z tej klasy (podając typy) i zarejestrować kilka lambd, aby wykonać konwersję. Oto przykład temperatury (z pewnymi fałszywymi obliczeniami):

enum Temperature
{
    Celcius,
    Fahrenheit,
    Kelvin
}

class TemperatureConverter : UnitConverter<Temperature, float>
{
    static TemperatureConverter()
    {
        BaseUnit = Temperature.Celcius;
        RegisterConversion(Temperature.Fahrenheit, v => v * 2f, v => v * 0.5f);
        RegisterConversion(Temperature.Kelvin, v => v * 10f, v => v * 0.05f);
    }
}

A potem korzystanie z niego jest dość proste:

var converter = new TemperatureConverter();

Console.WriteLine(converter.Convert(1, Temperature.Celcius, Temperature.Fahrenheit));
Console.WriteLine(converter.Convert(1, Temperature.Fahrenheit, Temperature.Celcius));

Console.WriteLine(converter.Convert(1, Temperature.Celcius, Temperature.Kelvin));
Console.WriteLine(converter.Convert(1, Temperature.Kelvin, Temperature.Celcius));

Console.WriteLine(converter.Convert(1, Temperature.Kelvin, Temperature.Fahrenheit));
Console.WriteLine(converter.Convert(1, Temperature.Fahrenheit, Temperature.Kelvin));
 22
Author: Danny Tuppeny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-21 16:56:53

Masz dobry początek, ale jak powiedział Jon, obecnie nie jest bezpieczny dla Typu; Konwerter nie ma sprawdzania błędów, aby upewnić się, że dziesiętny otrzymuje wartość Celsjusza.

Więc, aby pójść dalej, chciałbym zacząć wprowadzać typy struktur, które przyjmują wartość liczbową i stosują ją do jednostki miary. W architekturze korporacyjnej (tzw. Gang czterech wzorców projektowych) wzorzec ten nazywany jest wzorcem" pieniądza "(ang. "Money") po najczęściej używanym użyciu, aby określić kwotę danego rodzaju waluty. Wzorzec obowiązuje dla dowolnej ilości liczbowej, która wymaga, aby jednostka miary była znacząca.

Przykład:

public enum TemperatureScale
{
   Celsius,
   Fahrenheit,
   Kelvin
}

public struct Temperature
{
   decimal Degrees {get; private set;}
   TemperatureScale Scale {get; private set;}

   public Temperature(decimal degrees, TemperatureScale scale)
   {
       Degrees = degrees;
       Scale = scale;
   }

   public Temperature(Temperature toCopy)
   {
       Degrees = toCopy.Degrees;
       Scale = toCopy.Scale;
   }
}

Teraz masz prosty typ, który możesz użyć, aby wymusić, że konwersje, które dokonujesz, przyjmują temperaturę, która jest odpowiedniej skali, i zwracają temperaturę wyniku znaną jako inna skala.

Twoje Funcs będą potrzebować dodatkowej linii, aby sprawdzić, czy wejście pasuje do wyjścia; możesz nadal używać lambda, lub możesz pójść o krok dalej z simple Strategy pattern:

public interface ITemperatureConverter
{
   public Temperature Convert(Temperature input);
}

public class FahrenheitToCelsius:ITemperatureConverter
{
   public Temperature Convert(Temperature input)
   {
      if (input.Scale != TemperatureScale.Fahrenheit)
         throw new ArgumentException("Input scale is not Fahrenheit");

      return new Temperature(input.Degrees * 5m / 9m - 32, TemperatureScale.Celsius);
   }
}

//Implement other conversion methods as ITemperatureConverters

public class TemperatureConverter
{
   public Dictionary<Tuple<TemperatureScale, TemperatureScale>, ITemperatureConverter> converters = 
      new Dictionary<Tuple<TemperatureScale, TemperatureScale>, ITemperatureConverter>
      {
         {Tuple.Create<TemperatureScale.Fahrenheit, TemperatureScale.Celcius>,
            new FahrenheitToCelsius()},
         {Tuple.Create<TemperatureScale.Celsius, TemperatureScale.Fahrenheit>,
            new CelsiusToFahrenheit()},
         ...
      }

   public Temperature Convert(Temperature input, TemperatureScale toScale)
   {
      if(!converters.ContainsKey(Tuple.Create(input.Scale, toScale))
         throw new InvalidOperationException("No converter available for this conversion");

      return converters[Tuple.Create(input.Scale, toScale)].Convert(input);
   }
}

Ponieważ tego typu konwersje są dwukierunkowe, możesz rozważyć skonfigurowanie interfejsu tak, aby obsługiwał oba sposoby, za pomocą metody "ConvertBack" lub podobnej, która przyjmie temperaturę w skali Celsjusza i konwertuje do Fahrenheita. To zmniejsza liczbę klas. Wtedy, zamiast instancji klasy, wartości słownika mogą być wskaźnikami do metod na instancjach konwerterów. Zwiększa to nieco złożoność konfiguracji głównego TemperatureConverter strategy-selektor, ale zmniejsza liczbę klas strategii konwersji, które musisz zdefiniować.

Zauważ również, że sprawdzanie błędów odbywa się podczas wykonywania, gdy faktycznie próbujesz dokonać konwersji, wymagając, aby ten kod był dokładnie testowany we wszystkich zastosowaniach, aby upewnić się, że jest zawsze poprawny. Aby tego uniknąć, można wyprowadzić klasę temperatury bazowej, aby wytworzyć struktury CelsiusTemperature i FahrenheitTemperature, które po prostu definiowałyby ich skalę jako wartość stałą. Następnie ITemperatureConverter może być generyczny dla dwóch typów, obu temperatur, dając Ci sprawdzenie w czasie kompilacji, czy określasz konwersję, którą myślisz, że jesteś. TemperatureConverter można również dynamicznie znaleźć ITemperatureConverters, określić typy, między którymi będą konwertować, i automagicznie skonfigurować słownik konwerterów, więc nie musisz się martwić o dodawanie nowych. Odbywa się to kosztem zwiększonej liczby klas w zależności od temperatury; będziesz potrzebował czterech klasy domeny (podstawowa i trzy pochodne) zamiast jednej. Spowolni to również tworzenie klasy TemperatureConverter, ponieważ kod do refleksyjnego budowania słownika konwertera będzie używał sporo refleksji.

Można również zmienić liczby jednostek miary na "klasy markerów"; puste klasy, które nie mają żadnego znaczenia poza tym, że należą do tej klasy i wywodzą się z innych klas. Można wtedy zdefiniować pełną hierarchię klas "UnitOfMeasure", które reprezentują różne jednostki miary i mogą być używane jako argumenty typu ogólnego i ograniczenia; ITemperatureConverter może być generyczny dla dwóch typów, z których oba są ograniczone do klas TemperatureScale, a implementacja CelsiusFahrenheitConverter zamknie ogólny interfejs do typów CelsiusDegrees i FahrenheitDegrees, zarówno pochodzących z TemperatureScale. Które pozwala na ujawnienie samych jednostek miary jako ograniczeń konwersji, z kolei pozwalając konwersje między rodzajami jednostek miary (niektóre jednostki niektórych materiałów mają znane konwersje; 1 Brytyjski Imperialny kufel wody waży 1,25 funta).

Wszystkie z nich są decyzjami projektowymi, które uproszczą jeden rodzaj zmiany w tym projekcie, ale za pewnym kosztem (albo utrudniając coś innego lub zmniejszając wydajność algorytmu). To Ty decydujesz, co jest naprawdę "łatwe" dla Ciebie, w kontekście całego środowiska aplikacji i kodowania, w którym pracujesz do środka.

EDIT: użycie, które chcesz, z edycji, jest niezwykle łatwe dla temperatury. Jeśli jednak chcesz mieć ogólny konwerter Unit, który może pracować z dowolną jednostką miary, to nie chcesz już, Aby liczby były reprezentowane przez twoje jednostki miary, ponieważ liczby nie mogą mieć niestandardowej hierarchii dziedziczenia (pochodzą bezpośrednio z systemu.Enum).

Możesz określić, że domyślny konstruktor może przyjmować dowolne Enum, ale wtedy musisz upewnić się, że Enum jest jednym z typów, które są jednostka miary, w przeciwnym razie można by przekazać wartość DialogResult i konwerter by się zepsuł w czasie wykonywania.

Zamiast tego, jeśli chcesz jeden UnitConverter, który może przekonwertować na dowolną UnitOfMeasure podaną Lambda dla innych jednostek miary, określiłbym jednostki miary jako "klasy znaczników"; małe bezpaństwowe "tokeny", które mają znaczenie tylko w tym, że są ich własnym typem i pochodzą od ich rodziców:

//The only functionality any UnitOfMeasure needs is to be semantically equatable
//with any other reference to the same type.
public abstract class UnitOfMeasure:IEquatable<UnitOfMeasure> 
{ 
   public override bool Equals(UnitOfMeasure other)
   {
      return this.ReferenceEquals(other)
         || this.GetType().Name == other.GetType().Name;
   }

   public override bool Equals(Object other) 
   {
      return other is UnitOfMeasure && this.Equals(other as UnitOfMeasure);
   }    

   public override operator ==(Object other) {return this.Equals(other);}
   public override operator !=(Object other) {return this.Equals(other) == false;}

}

public abstract class Temperature:UnitOfMeasure {
public static CelsiusTemperature Celsius {get{return new CelsiusTemperature();}}
public static FahrenheitTemperature Fahrenheit {get{return new CelsiusTemperature();}}
public static KelvinTemperature Kelvin {get{return new CelsiusTemperature();}}
}
public class CelsiusTemperature:Temperature{}
public class FahrenheitTemperature :Temperature{}
public class KelvinTemperature :Temperature{}

...

public class UnitConverter
{
   public UnitOfMeasure BaseUnit {get; private set;}
   public UnitConverter(UnitOfMeasure baseUnit) {BaseUnit = baseUnit;}

   private readonly Dictionary<UnitOfMeasure, Func<decimal, decimal>> converters
      = new Dictionary<UnitOfMeasure, Func<decimal, decimal>>();

   public void AddConverter(UnitOfMeasure measure, Func<decimal, decimal> conversion)
   { converters.Add(measure, conversion); }

   public void Convert(UnitOfMeasure measure, decimal input)
   { return converters[measure](input); }
}

Można umieścić w Sprawdzanie błędów (Sprawdź, czy jednostka wejściowa ma konwersja określona, sprawdź, czy dodawana konwersja jest dla UOM z tym samym rodzicem co typ bazowy, itd itd.) według własnego uznania. Można również uzyskać UnitConverter do tworzenia TemperatureConverter, pozwalając na dodawanie statycznych, kompilacyjnych sprawdzeń typu i unikanie kontroli czasu pracy, których UnitConverter musiałby używać.

 5
Author: KeithS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-21 18:07:23

To brzmi jakbyś chciał coś w stylu:

Func<decimal, decimal> celsiusToKelvin = x => x + 273.15m;
Func<decimal, decimal> kelvinToCelsius = x => x - 273.15m;
Func<decimal, decimal> fahrenheitToKelvin = x => ((x + 459.67m) * 5m) / 9m;
Func<decimal, decimal> kelvinToFahrenheit = x => ((x * 9m) / 5m) - 459.67m;

Możesz jednak rozważyć nie tylko użycie decimal, ale posiadanie typu, który zna jednostki, więc nie możesz przypadkowo (powiedzmy) zastosować konwersji "Celsjusza do Kelvina" do wartości niecelsjusza. Prawdopodobnie przyjrzyj się podejściu F# jednostki miary do inspiracji.

 3
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-21 15:13:40

Możesz spojrzeć na Units.NET.jest na GitHub i NuGet. Zapewnia najpopularniejsze jednostki i konwersje, obsługuje zarówno statyczne pisanie i wyliczanie jednostek, jak i parsowanie / drukowanie skrótów. Nie analizuje jednak wyrażeń i nie można rozszerzać istniejących klas jednostek, ale można rozszerzać je o nowe jednostki innych firm.

Przykładowe konwersje:

Length meter = Length.FromMeters(1);
double cm = meter.Centimeters; // 100
double feet = meter.Feet; // 3.28084
 1
Author: angularsen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-30 13:26:44

Normalnie chciałem dodać to jako komentarz do postu Danny 'ego Tuppeny' ego, ale wygląda na to, że nie jestem w stanie dodać tego jako komentarz.

Poprawiłem nieco rozwiązanie z @ Danny Tuppeny. Nie chciałem dodawać każdej konwersji z dwoma czynnikami rozmowy, ponieważ tylko jeden powinien być konieczny. Również parametr typu Func nie wydaje się być konieczny, tylko sprawia, że jest bardziej skomplikowany dla użytkownika.

Więc mój telefon wygląda tak:

public enum TimeUnit
{
    Milliseconds,
    Second,
    Minute,
    Hour,
    Day,
    Week
}

public class TimeConverter : UnitConverter<TimeUnit, double>
{
    static TimeConverter()
    {
        BaseUnit = TimeUnit.Second;
        RegisterConversion(TimeUnit.Milliseconds, 1000);
        RegisterConversion(TimeUnit.Minute, 1/60);
        RegisterConversion(TimeUnit.Hour, 1/3600);
        RegisterConversion(TimeUnit.Day, 1/86400);
        RegisterConversion(TimeUnit.Week, 1/604800);
    }
}

Dodałem również metodę aby uzyskać Współczynnik konwersji między jednostkami do. Jest to zmodyfikowana Klasa UnitConverter:

/// <summary>
/// Generic conversion class for converting between values of different units.
/// </summary>
/// <typeparam name="TUnitType">The type representing the unit type (eg. enum)</typeparam>
/// <typeparam name="TValueType">The type of value for this unit (float, decimal, int, etc.)</typeparam>
/// <remarks>http://stackoverflow.com/questions/7851448/how-do-i-create-a-generic-converter-for-units-of-measurement-in-c
/// </remarks>
public abstract class UnitConverter<TUnitType, TValueType> where TValueType : struct, IComparable, IComparable<TValueType>, IEquatable<TValueType>, IConvertible
{
    /// <summary>
    /// The base unit, which all calculations will be expressed in terms of.
    /// </summary>
    protected static TUnitType BaseUnit;

    /// <summary>
    /// Dictionary of functions to convert from the base unit type into a specific type.
    /// </summary>
    static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsTo = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();

    /// <summary>
    /// Dictionary of functions to convert from the specified type into the base unit type.
    /// </summary>
    static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsFrom = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();

    /// <summary>
    /// Converts a value from one unit type to another.
    /// </summary>
    /// <param name="value">The value to convert.</param>
    /// <param name="from">The unit type the provided value is in.</param>
    /// <param name="to">The unit type to convert the value to.</param>
    /// <returns>The converted value.</returns>
    public TValueType Convert(TValueType value, TUnitType from, TUnitType to)
    {
        // If both From/To are the same, don't do any work.
        if (from.Equals(to))
            return value;

        // Convert into the base unit, if required.
        var valueInBaseUnit = from.Equals(BaseUnit)
                                ? value
                                : ConversionsFrom[from](value);

        // Convert from the base unit into the requested unit, if required
        var valueInRequiredUnit = to.Equals(BaseUnit)
                                ? valueInBaseUnit
                                : ConversionsTo[to](valueInBaseUnit);

        return valueInRequiredUnit;
    }

    public double ConversionFactor(TUnitType from, TUnitType to)
    {
        return Convert(One(), from, to).ToDouble(CultureInfo.InvariantCulture);
    }

    /// <summary>
    /// Registers functions for converting to/from a unit.
    /// </summary>
    /// <param name="convertToUnit">The type of unit to convert to/from, from the base unit.</param>
    /// <param name="conversionToFactor">a factor converting into the base unit.</param>
    protected static void RegisterConversion(TUnitType convertToUnit, TValueType conversionToFactor)
    {
        if (!ConversionsTo.TryAdd(convertToUnit, v=> Multiply(v, conversionToFactor)))
            throw new ArgumentException("Already exists", "convertToUnit");

        if (!ConversionsFrom.TryAdd(convertToUnit, v => MultiplicativeInverse(conversionToFactor)))
            throw new ArgumentException("Already exists", "convertToUnit");
    }

    static TValueType Multiply(TValueType a, TValueType b)
    {
        // declare the parameters
        ParameterExpression paramA = Expression.Parameter(typeof(TValueType), "a");
        ParameterExpression paramB = Expression.Parameter(typeof(TValueType), "b");
        // add the parameters together
        BinaryExpression body = Expression.Multiply(paramA, paramB);
        // compile it
        Func<TValueType, TValueType, TValueType> multiply = Expression.Lambda<Func<TValueType, TValueType, TValueType>>(body, paramA, paramB).Compile();
        // call it
        return multiply(a, b);
    }

    static TValueType MultiplicativeInverse(TValueType b)
    {
        // declare the parameters
        ParameterExpression paramA = Expression.Parameter(typeof(TValueType), "a");
        ParameterExpression paramB = Expression.Parameter(typeof(TValueType), "b");
        // add the parameters together
        BinaryExpression body = Expression.Divide(paramA, paramB);
        // compile it
        Func<TValueType, TValueType, TValueType> divide = Expression.Lambda<Func<TValueType, TValueType, TValueType>>(body, paramA, paramB).Compile();
        // call it
        return divide(One(), b);
    }

    //Returns the value "1" as converted Type
    static TValueType One()
    {
        return (TValueType) System.Convert.ChangeType(1, typeof (TValueType));
    }
}
 0
Author: Max,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-24 14:37:34

Można zdefiniować typ ogólny jednostek fizycznych taki, że jeśli ktoś ma dla każdej jednostki typ, który implementuje {[0] } i zawiera metodę tłumaczenia między tą jednostką a "jednostką bazową" tego typu, można wykonać arytmetykę na wartościach, które są wyrażone w różnych jednostkach i mają je konwertowane w razie potrzeby, przy użyciu systemu typu takiego, że zmienna typu AreaUnit<LengthUnit.Inches> będzie akceptować tylko rzeczy wymiarowane w calach kwadratowych, ale jeśli jeden powiedział {[2] } to automatycznie przetłumaczyć te inne jednostki przed wykonaniem mnożenia. W rzeczywistości może to działać całkiem dobrze przy użyciu składni wywołania metody, ponieważ metody takie jak Metoda {[3] } mogą akceptować ogólne parametry typu ich operandów. Niestety, nie ma sposobu, aby operatory były ogólne, ani nie ma sposobu, aby Typ taki jak AreaUnit<T> where T:LengthUnitDescriptor zdefiniował sposób konwersji do lub z innego arbitralnego typu ogólnego AreaUnit<U>. {[6] } może definiować konwersje do i z np. AreaUnit<Angstrom>, ale nie ma możliwości, aby kompilator powiedział, że kod, który jest biorąc pod uwagę AreaUnit<Centimeters> and wantsAreaUnit ' może konwertować cale na angstromy, a następnie na centymetry.

 0
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-24 16:10:32