0

Continuing with the answer given here, is there a way to allow MyChildSingleton to be abstract, and then let it's sub-classes define their own constructors? An example of what I would like to do:

public abstract class SingletonBase<T> 
    where T : SingletonBase<T>, new()
{
    private static T _instance = new Lazy<T>(() => new T());
    public static T Instance
    {
        get
        {                
            return _instance;
        }   
    }
}

public abstract class DefaultConfigData: SingletonBase<DefaultConfigData>
{
    // This class won't compile since it's abstract, and SingletonBase<T>
    // has a new() restriction on T
    // Also, this class is immutable and doesn't change state
    public virtual string SomeData { get; } = "My Default Data String";

    public virtual double MoreData { get; } = 2.71;

    public virtual double SomeFunction(double num)
        { return num + 2*MoreData; }
    public DefaultConfigData() { ; /* nothing to do here */ }

    // Another 50 or so default values/functions...
    // enough to be tedious to redefine in multiple places,
    // and adding a constructor that takes every value would be ridiculous.
    // It would be possible to encapsulate this data, but I'm not
    // yet sure how this should be done, so I haven't gone there yet
}

public class SpecificConfigData1: DefaultConfigData
{
    public override string SomeData { get; } = "A Different String";
    public SpecificConfigData1() { ; /* nothing to do here */ }
}

public class SpecificConfigData2: DefaultConfigData
{
    public override double MoreData { get; } = 3.14;
    public SpecificConfigData2() { ; /* nothing to do here */ }
}

// Downstream developers may need to define additional ConfigData classes

public class Library
{
    public static double doSomething(DefaultConfigData data) { return data.MoreData + 2.0; }
}

public class Program
{
    private readonly DefaultConfigData data;
    public Program(bool choice)
    {
        data = (choice) ? SpecificConfigData1.Instance : SpecificConfigData2.Instance;
    }

    public static void Main()
    {
        Program prog = new Program(/*run-time user input here*/);
        Console.PrintLine(Library.doSomething(prog.data));
    }
}

Using a singleton pattern seemed like a good idea, because for each specific subclass the data only needs to exist in one place, and since it's immutable this avoids most of the issues associated with singletons (global mutable state, etc.). Providing the singleton functionality in an abstract base class would avoid the boilerplate of putting the private instance and public get property, which is what I'm doing now in each sub-class. This really isn't too onerous a requirement, I'm sure I could live with it.

I don't want to make DefaultConfigData and it's data static, because then I can't inherit from it and have my library functions know how to interact with it (no support for metaclasses in C#). Also, I don't want to use an interface, because so much of the functionality is shared, and I couldn't define that in the interface.

I would also welcome comments on alternative approaches, if the one I'm trying to do can't be accomplished, or if another way is simply easier. I know that a factory pattern could also work here, and that's something I intend to try eventually.

Last, why is this even an issue? Why wasn't the decision made to let abstract classes satisfy the new() requirement, provided that any of their sub-classes also satisfy a new()?

Note that my "users" are other internal developers/my future self. Source code is usually the deliverable, and pushing checks to run time are ok in this environment.

Community
  • 1
  • 1
Evan Cox
  • 187
  • 1
  • 14

1 Answers1

3

The easiest solution I can think of is using a factory pattern. The other solution is you need to keep DefaultConfigData class generic, like:

public abstract class DefaultConfigData<T>: SingletonBase<T>
    where T : DefaultConfigData<T>, new()
{ }

The problem with that is when you want to use DefaultConfigData anywhere, you have to make that method or class generic, like the doSomething method:

public static double doSomething<T>(DefaultConfigData<T> data)
    where T : DefaultConfigData<T>, new()
{
    return data.MoreData + 2.0;
}

And as you can guess, that can get really annoying. So, back to the factory pattern:

public static class MyFactory<T>
{
    private static Lazy<T> _instance = new Lazy<T>(CreateUsingReflection);
    public static T Instance
    {
        get
        {
            return _instance.Value;
        }
    }

    private static T CreateUsingReflection()
    {
        BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
        ConstructorInfo ctor = typeof(T).GetConstructor(flags, null, Type.EmptyTypes, null);
        return (T)ctor.Invoke(null);
    }
}

And, you can use it like so:

SpecificConfigData1 scd1 = MyFactory<SpecificConfigData1>.Instance;

Note that none of your classes need to inherit from MyFactory. You are free to create classes that inherit from anything (or nothing), as long as you have a parameterless constructor. You could restrict the T in MyFactory<T> using where T : new(), in which case you will get compile-time guarantee that the class supports a parameterless public constructor, and can avoid reflection by creating the class using just new T(). Without the new() restriction, it can create singletons using a private constructor, but you lose the compile-time checking that a parameterless constructor exists.

Actually, there is another solution. It is much more complex, but it is quite powerful if you use it to its fullest: an IoC container such as Autofac, Unity, and many others. These can help manage your instances, so that you can specify the Types that should be singletons. I won't go over how to use one since that is a whole different topic, and would require multiple paragraphs just to explain the basics.

Mayoor
  • 181
  • 5
  • Thanks for the quick response. I suspected that the factory pattern was the way to go. I've also heard of IoC containers, which I may eventually use. I'm a hardware guy living in a SW world, so moving to an IoC solution is outside the scope of what I can accomplish in this project right now. I would like to avoid using reflection, so for now I'll try to use it with `where T : new()`. This opens up the possibility of someone creating more than instance if they don't use the factory, correct? In this case, it would only be a performance hit, and the code would still function, I think. – Evan Cox Sep 23 '15 at 16:05
  • Yes, that is correct; restricting it with `new()` forces all of the singletons to have a public constructor. Due note that reflection in this case isn't going to create a big performance hit: it is only used the first time an instance is needed. – Mayoor Sep 23 '15 at 18:31