How to create a derived class from a base class in general

I have expanded the converter class specified in this post: https://stackoverflow.com/a/3186262/2326322: This is what I have now:

public abstract class UnitBase<TUnitType, TValueType> where TUnitType : struct, IComparable, IConvertible, IFormattable { protected static TUnitType BaseUnit; protected static TValueType BaseValue; private static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsTo = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>(); private static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsFrom = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>(); public static 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; } 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"); } public static string GetUnit(TUnitType unit) { var type = typeof(TUnitType); if (!type.IsEnum) { throw new ArgumentException(); } return Enums.GetEnumDescription(unit as Enum); } public TValueType InUnit(TUnitType unit) { return Convert(BaseValue, BaseUnit, unit); } } 

I have specific implementations like this:

 public enum TemperatureUnit { [System.ComponentModel.Description("°C")] Celcius, [System.ComponentModel.Description("°F")] Fahrenheit } public class Temperature : UnitBase<TemperatureUnit, double> { static Temperature() { BaseUnit = TemperatureUnit.Celcius; RegisterConversion(TemperatureUnit.Fahrenheit, x => x * 1.8d + 32d, x => (x - 32d) / 1.8d); } private Temperature(double value) { BaseValue = value; } public static Temperature FromUnit(double value, TemperatureUnit unit) { return new Temperature(Convert(value, unit, BaseUnit)); } } 

I managed to make the generic ToUnit method part of the base class, but with the FromUnit method I now have one for the specific class (and they all look very similar). I want to be able to do this:

 Temperature temperature = Temperature.FromUnit(10, TemperatureUnit.Celcius); Pressure pressure = Pressure.FromUnit(50, PressureUnit.Bar); 

... without having to execute FromUnit in each particular class. Since this is a static method, I cannot force the implementation to use the interface. I could create a public constructor in specific classes and have something like:

 public static T FromUnit<T>(TValueType value, TUnitType unit) where T : new() { BaseValue = Convert(value, unit, BaseUnit); return new T(); } 

But I think this is not so good, and I have to go:

 Temperature temperature = Temperature.FromUnit<Temperature>(10, TemperatureUnit.Celcius); 

<Temperature> I find it unnecessary.

Is it possible to implement a more general FromUnit method?

Edit: Basically, I want, instead of executing FromUnit in each subclass:

 public static Temperature FromUnit(double value, TemperatureUnit unit) { return new Temperature(Convert(value, unit, BaseUnit)); } public static Pressure FromUnit(double value, PressureUnit unit) { return new Pressure(Convert(value, unit, BaseUnit)); } public static Speed FromUnit(double value, SpeedUnit unit) { return new Speed(Convert(value, unit, BaseUnit)); } ... 

... to host the FromUnit common class in the base class.

+4
source share
2 answers

The parameter <Temperature> type in your "not very pleasant" solution may seem redundant, but in fact it is not. When you write

 Temperature temperature = Temperature.FromUnit<Temperature>(10, TemperatureUnit.Celcius); 

The code is actually translated into

 Temperature temperature = UnitBase<TemperatureUnit, double>.FromUnit<Temperature>(10, TemperatureUnit.Celcius); 

The compiler is good enough that you can use Temperature to access this method. Since it actually calls UnitBase.FromUnit() , it does not suspect that you really want to use Temperature , and therefore you should specify it as an additional type parameter.

+1
source

Why not move the FromUnit implementation to UnitBase:

  public static TValueType FromUnit(TValueType value, TUnitType unit) { return Convert(value, BaseUnit, unit); } 
0
source

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


All Articles