Workaround for new() constraint with parameters in generics

I know, there are a few answers here on SO, which seem to solve my questions, like this and that threads. But in my specific case, there is some difference.

In front my question: Is this a possible/intelligent workaround to manage the new() constraint in generics with parameters?

Assume the following base class:

abstract class BaseClass
{
    internal BaseClass()
    {
        Console.WriteLine("{0}'s ctor (parameterless)", "BaseClass");
    }

    internal BaseClass(object parent)
    {
        Console.WriteLine("{0}'s ctor", "BaseClass");
        Parent = parent;
    }

    public object Parent { get; private set; }
}

and interface:

interface IGenerate
{
    IGenerate GenerateNew(int x, object parent);
}

The base class is only intended to store a parent object, the interface provides a method to return an object of the implementing class calling its constructor, like this:

class ClassX : BaseClass, IGenerate
{
    public ClassX()
    {
        Console.WriteLine("{0}'s ctor (parameterless)", "ClassX");
    }

    public ClassX(int x, object parent)
        : base(parent)
    {
        Console.WriteLine("{0}'s ctor", "ClassX");
        X = x;
    }

    public IGenerate GenerateNew(int x, object parent)
    {
        Console.WriteLine("{0}.GenerateNew()", "ClassX");
        return new ClassX(x, parent);
    }

    public int X { get; private set; }
}

My generic class is intended to generate and store an object of the provided class calling the interfaces method:

class MyGeneric<T> : BaseClass where T : IGenerate, new()
{
    public MyGeneric(int x, object parent)
        : base(parent)
    {
        Console.WriteLine("{0}'s ctor", "MyGeneric");
        Instance = new T().GenerateNew(x, this);
    }

    public IGenerate Instance { get; private set; }
}

Another class inherits the generic:

class ClassXSpecifier : MyGeneric<ClassX>
{
    public ClassXSpecifier(int x, object parent)
        : base(x, parent)
    {
        Console.WriteLine("{0}'s ctor", "ClassXSpecifier");
    }
}

The use of these constructs is something like that:

var classXspecifier = new ClassXSpecifier(5, null);
var classX = (ClassX)classXspecifier.Instance;
Console.WriteLine(classX.X);

Output:

BaseClass's ctor
MyGeneric's ctor
BaseClass's ctor (parameterless)
ClassX's ctor (parameterless)
ClassX.GenerateNew()
BaseClass's ctor
ClassX's ctor
ClassXSpecifier's ctor
5

Again my primary question: Is this a possible/intelligent workaround to manage the new() constraint in generics with parameters?

A secondary question: Why do BaseClass and ClassX need to have a parameterless constructor while they won’t be used in any case explicitly? If I remove them, I get the following error:

‘ClassX’ must be a non-abstract type with a public parameterless constructor in order to use it as parameter ‘T’ in the generic type or method ‘MyGeneric’

Thanks in advance, Christian =)

!!! SOLUTION !!!

The provided answer tempt me to do modifications, that the new() constraint could be removed -> so the parameterless constructors could be removed, too.

I deleted the interface and added a static method into BaseClass to generate new objects:

public static BaseClass GenerateNew(Type T, object[] args)
{
    return (BaseClass)Activator.CreateInstance(T, args);
}

So the generic class could be reduced to

class MyGeneric<T> : BaseClass
{
    public MyGeneric(int x, object parent)
        : base(parent)
    {
        Console.WriteLine("{0}'s ctor", "MyGeneric");
        Instance = GenerateNew(typeof(T), new[] { x, parent });
    }

    public BaseClass Instance { get; private set; }
}

That was it, thanks to all comments, too!

Answer

Question
Again my primary question: Is this a possible/intelligent workaround to manage the new() constraint in generics with parameters?

Answer
Your passing a type(ClassX) and want to access an instance function(GenerateNew) without creating an instance -> well that’s one problem you need to think about. You can create a static factory(andor use IOC) for creating new object’s by types.

Question
Why do BaseClass and ClassX need to have a parameterless constructor while they won’t be used in any case explicitly?

Answer
This constraint requires that the generic type that is used is non-abstract and that it has a default (parameterless) constructor allowing you to call it. BTW, you are using the empty ctor by doing new T().

Leave a Reply

Your email address will not be published. Required fields are marked *