0

The very-well known constraint in generic types is new(), it is possible to add parameters like (args,args,...) to force the compiler to check that the class contains a particular constructor?

The block shows you the case.

public class FooType
{
    public FooType(int arg1)
    { 
    
    }
}

public sealed class FooTypeChildA : FooType
{
    public FooTypeChildA(int a) : base(a)
    { 
    
    }
}

public sealed class FooTypeChildB : FooType
{
    public FooTypeChildB() : base(0)
    { 
    
    }
}

//public class FooConstraint<T> where T : FooType , new() // SUCCESS
//public class FooConstraint<T> where T : FooType , new(int) // ERROR

public class FooConstraint<T> where T : FooType // usually the constraint is "new()", but I need something like this: new(int) that the compiler verify the CHECK_1
{
    
}

public sealed class Passed : FooConstraint<FooTypeChildA> //[CHECK_1] Pass the constraint.
{

}

public sealed class NotPassed : FooConstraint<FooTypeChildB> //[CHECK_1] Not Pass the constraint. 
{

}

This is instruction shows a possible syntax exception, new(int arg1)

Is not important the way that the generic instance invokes the constructor because a basic reflection solves the problem at run-time, but the idea is to force a Compiler error.


ATTEMPT#1 - This is not possible; because Interfaces must not specify constructors.

public interface IFoo
{
    IFoo(int a); // Error here CS0526
}

ATTEMPT#2 - The original question was closed because the moderator was focused in solve the problem in runtime level instead of compilation-time level.

This Question is not duplicated in this question: Because of (T)Activator.CreateInstance( typeof(T) , args ) is NOT an option when you need a really strict compilation check. It is obvious that Activator not accomplish the basic principle (Derived classes and inherited behaviors must be used as to be possible instead of theirs final implementations - Liskov) that warranty Child Type have the behavior of the Parent Type even at compilation level.


Fabio Andrés
  • 229
  • 2
  • 11
  • 2
    How about adding a `Func` parameter to the `Foo` constructor, as a way of representing "how to create a `T` using an `int`"? Then `Foo` knows how to create a `T` using an `int`. – Sweeper Mar 25 '21 at 10:31
  • Adding `new()` only means that the type has a public parameterless constructor. It sounds like you need to investigate the factory pattern. – Neil Mar 25 '21 at 10:31
  • [2] Factory pattern solve the problem at runtime instead of compilation-time. It is basically the same that implement an Activator. – Fabio Andrés Mar 25 '21 at 10:38
  • Is it possible to have an abstract class here? – Klamsi Mar 25 '21 at 10:43
  • of course an abstract class is valid when it forces the verification, but is not clear for me its implementation, do you have a suggestion? – Fabio Andrés Mar 25 '21 at 11:04
  • 1
    I hope I got your problem right. In an abstract class you can define a specific constructor – Klamsi Mar 25 '21 at 11:22
  • 1
    Factory pattern **is SOLID** because you can enforce the interface/delegate type of the factory object – Charlieface Mar 25 '21 at 12:12
  • Of course, no discussion, Factory Pattern is really SOLID bassed, "is NOT an option when you need a really strict compilation check." I will edit the comment it sounds like Factory is not SOLID my idea is focused on compilation-level. – Fabio Andrés Mar 25 '21 at 12:45

1 Answers1

1

The very-well known constraint in generic types is new(), it is possible to add parameters like (args,args,...) to force the compiler to check that the class contains a particular constructor?

While it feels like this should be available, it isn't. You can only add a constraint for a parameterless constructor.

As an alternative, consider passing a factory function. Here, you can specify the input arguments.

public class FooConstraint<T> where T : FooType
{
    private readonly T _myObject;

    public FooConstraint(Func<int, T> generator)
    {
        _myObject = generator(123);
    }
}

Note: you could also store the Func itself instead of immediately using it and discarding it. This is just an example.

Since the FooConstraint class definition explicitly defines the Func<> generic parameters, you can ensure that the consumer of FooConstraint is aware exactly which constructor parameters your FooConstraint will use to instantiate a T instance.

var myConstraint = new FooConstraint<FooTypeChildA>(myInt => new FooTypeChildA(myInt));

Since you'll always just be calling a constructor and never doing anything fancy with the Func, this feels like a bit of boilerplate, but given the absence of a true generic parametered constructor constraint, this is the closest alternative.

Flater
  • 12,908
  • 4
  • 39
  • 62
  • :( I just check the grammar, unfortunately, you are right, is not a feature of the syntax, in a compilation level is not possible. I have workaround such as Functional Builders but all of them only solve the problem at run-time level... all implementations are not strict during the compilation. – Fabio Andrés Mar 25 '21 at 11:33
  • public class FooConstraint where T : FooType , new(Func) // is not supported... – Fabio Andrés Mar 25 '21 at 11:42
  • @FabioAndrés _"public class FooConstraint where T : FooType , new(Func) // is not supported..."_ The `Func` I suggested wasn't intended to be used as a generic constraint, but rather as an alternative to using such a generic constraint (which sadly doesn't exist). It covers most of your needs, as much as is possible in the language currently. – Flater Mar 25 '21 at 11:51
  • right! I thank you very much for your comment however I'm still crying because I'm very obsessive with a clear and strict code. – Fabio Andrés Mar 25 '21 at 11:53