Access to the static property of the child in the parent method.

I have a similar problem with the message Accessing the static property of a child in the parent method . The preferred answer indicates that the design of the classes is faulty and additional information is needed to discuss the problem.

Here is the situation that I want to discuss with you.

I want to implement some data types, such as length, mass, current, ... There must be an implicit cast to instantiate from a given string. For example, the example โ€œ1.5 mโ€ should give the same thing as โ€œ150 cmโ€, or โ€œ20 inchesโ€ should be processed correctly.

To be able to convert between different units, I need quantitative conversion constants. My idea was to create an abstract base class with some static translation methods. To do their job, they must use a classically defined static dictionary. Therefore, consider an example.

public class PhysicalQuantities { protected static Dictionary<string, double> myConvertableUnits; public static double getConversionFactorToSI(String baseUnit_in) { return myConvertableUnits[baseUnit_in]; } } public class Length : PhysicalQuantities { protected static Dictionary<string, double> myConvertableUnits = new Dictionary<string, double>() { { "in", 0.0254 }, { "ft", 0.3048 } }; } class Program { static void Main(string[] args) { Length.getConversionFactorToSI("in"); } } 

I think this makes for quite intuitive use and keeps the code compact and fully readable and extensible. But, of course, I ran into the same problems that the link mentions.

Now my question is: how can I avoid these design issues?

+6
source share
3 answers

I think this can be solved with generics that still look readable. Refined as suggested by Slaks to match registration in a static constructor to make it thread safe as such.

So, if I am not mistaken:

  • thread safety (all dictionary work in a static constructor)
  • the syntax is still easy to use and read by SIConversion<Length>.GetFactor() (1 char more)
  • the code required for implementation on derived classes is very template register(string,double); (actually shorter than dictionary definition)

     interface ISIConversionSubscriber { void Register(Action<string, double> regitration); } static class SIConversion<T> where T : ISIConversionSubscriber, new() { private static Dictionary<string, double> myConvertableUnits = new Dictionary<string, double>(); static SIConversion() { T subscriber = new T(); subscriber.Register(registrationAction); } public static double GetFactor(string baseUnit) { return myConvertableUnits[baseUnit]; } private static void registrationAction(string baseUnit, double value) { myConvertableUnits.Add(baseUnit, value); } } abstract class PhysicalQuantities : ISIConversionSubscriber { public abstract void Register(Action<string, double> register); } class Length : PhysicalQuantities { public override void Register(Action<string, double> register) { // for each derived type register the type specific values in this override register("in", 1); } } class Program { static void Main(string[] args) { Console.WriteLine(SIConversion<Length>.GetFactor("in")); } } 

Output: 1

If you are wondering why I did a PhysicalQuantities abstract: to avoid using it with SIConversion<PhysicalQuantities>.GetFactor() , since we donโ€™t have a conversion for the base class. You probably don't need instances of a base class like this, this is not a complete representation of the quantity, so it will probably only contain reusable methods.

Another suggestion is to use Enum for baseUnit instead of a string. Since everyone is aiming for a type of security and a crying trick over magical strings, this is probably a good way :))

+4
source

The best option here, as a rule, is to avoid the static nature of your design and work with instances instead. I have a similar library that I developed, but usage tends to resemble more:

 static void Main() { // Since I'm working with instances, I tend to pass the actual // measured amount in as well... However, you could leave this out (or pass 1) var len = Length.Create(42, "in"); double conversionFactory = len.ConversionFactorToSI; } 

I designed it so that I could define a dictionary for each type, statically, but use the factory method, which passes this to the base class constructor (which is protected). This allows you to create a base class with a reference to a dictionary of a certain type, which works very cleanly.

+3
source

I found that test-based development often led me to better projects. In your case, "translation methods" are an important part that you want to test regardless of their use in your classes. I would suggest encapsulating this logic in its own type.

You can then focus on the examples that Reed offers, knowing that your translation logic is well tested. Your abstract base class then simply serves as a common root that knows how to get the right translator.

+2
source

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


All Articles