0

In c# I want to write extension methods for the DateTime struct to calculate date differences in years, month, etc.

For being prepared to change the calculation of the date differences I made an interface for this an passed it to a configuration method in the static extension class.

In concrete:

public static class DateTimeExtension
{

    private static IDateTimeDifference _dateDifference;


    public static void Configure(IDateTimeDifference dateDifference )
    {
        DateTimeExtension._dateDifference = dateDifference;
    }


    public static int DiffFromInYears(this DateTime dateFromIncl, DateTime dateToExcl)
    {
        return _dateDifference.InYears(dateFromIncl, dateToExcl);
    }

    public static int DiffToInYears(this DateTime dateToExcl, DateTime dateFromIncl)
    {
        return _dateDifference.InYears(dateFromIncl, dateToExcl);
    }

}

My configuration of the services looks like this:

var host = Host
            .CreateDefaultBuilder()
            .ConfigureServices(
            (context, services) =>
            {
                services.AddSingleton<IDateTimeDifference>( s => new DateDifferenceFullPeriods() );
            }
            )
            .Build();

DateTimeExtension.Configure( host.Services.GetRequiredService<IDateTimeDifference>() );

I want to get as low performance penalties as possible.

Is there a way to enforce the user of my extension methods to call the Configure method at startup?

Would this code be considered testable? How do I test the extension methods? Is it enough to test the implementations of IDateTimeDifference interface?

In general are extension methods a good approach for this problem, considered I don't want to pass a IDateTimeDifference parameter in each difference calculation method.

Are there other methods of injection more suitable? Is dependency injection even necessary, if I don't need to change the difference calculation at run time?

ChopSeo
  • 1
  • 1
  • Why do you need your time-difference logic to be loosely coupled? In other words, why do you need to hide this logic behind an `IDateTimeDifference` interface? Does it contain any non-deterministic behavior? Does it do any I/O? Do you want to swap, decorate, or intercept this behavior later on? It seems a simple deterministic calculation to me where tight coupling is not a problem. Am I missing something? – Steven Apr 12 '21 at 07:08
  • Related: https://stackoverflow.com/q/52325438/264697 – Steven Apr 12 '21 at 07:12
  • Related: https://stackoverflow.com/questions/55213803/use-dependency-injection-in-static-class – Steven Apr 12 '21 at 07:13
  • Yes, I want to swap this behavior. I think, it is sufficient to choose this behavior at startup. Mostly for different rounding or consideration of leap years. Maybe that's over engineered. Perhaps I should just add rounding styles as a parameter to extension methods... I thought, there maybe is a clever pattern, to solve this kind of problems. – ChopSeo Apr 12 '21 at 13:53

1 Answers1

1
  1. Do you actually need an extension method for this? Can't you just use the TimeSpan struct https://learn.microsoft.com/en-us/dotnet/api/system.timespan?view=net-5.0 to calculate time differences in days, months, years?
  2. Extension methods are by their nature static and they are defined in static classes. On the other hand dependency injection is used to register a specific instance of a class and provide this specific instance of a class normally through an interface to other classes or methods that require it. But since extension methods are defined in static classes and are static you don't need to register an instance of a static class since static classes are loaded automatically when they are required. Generally, I don't see any logic to use dependency injection for extension methods. Extension methods must rely only on their parameters and the this object. They should not have any other external dependency.
  3. More on defining extension methods for structs you can read here Extension methods on a struct
Anton Kovachev
  • 322
  • 3
  • 6
  • 1. Extension methods are not carved in stone... Especially considering your comments in 2. and 3. but I need different types of difference calculations. Different rounding, different consideration of leap years and so on. 2. is a valid point. Perhaps I just go with the IDateDifference service alone and skip the extension methods. I just thought, I may be missing a clever pattern people came around with to solve this kind of problems. 3. interesting link which reminds me of the value type of DateTime. Thx. – ChopSeo Apr 11 '21 at 20:39