This project is read-only.

Generic condition "where : new()" with parameter

Topics: C# Language Design
Apr 7, 2014 at 10:58 AM
I had this many times where i would new up an instancen of a generic parameter T but this only works if the type Argument given for T has a Default constructor. So if that is not the case you have to fall back to Activator.CreateNewInstance().

I really would like to specify all the different constructors a type argument for T must provide to be used with my generic.
class Gen<T>
  where T : new(string), new(int), new()
The given type argument for T must then at least provide the following constructors
class Example
{
 Example() {}
 Example(string s) {}
 Example(int i) {}
}
Apr 7, 2014 at 3:56 PM
I believe this would require either changing the CLR or some workaround. Because the CLR currently supports only the equivalent of the new() constraint, but nothing more.
Apr 7, 2014 at 6:30 PM
Yes, if reflection is needed for these constraints, otherwise the Compiler could Emit Activator.CreatInstance statements as needed and checking the constraints without Emiting these into signatures in the assembly.
Apr 9, 2014 at 6:34 PM
Yes, anything that could be done along these lines would be a very highly appreciated feature. It would eliminate the need for an awful lot of trivial factory classes.
Apr 9, 2014 at 6:41 PM
If implementing this requires changes in CLR, I would expect a more generic change, which includes not just constructors, but arbitrary static methods on type. This could include e. g. arithmetic operators as well.
Apr 9, 2014 at 7:30 PM
This is ultimately the same thing as "static interface members" from your previous thread. There's no real difference between constructors and static methods when it comes to static members and constructors.
Apr 10, 2014 at 4:52 PM
mdanes wrote:
This is ultimately the same thing as "static interface members" from your previous thread. There's no real difference between constructors and static methods when it comes to static members and constructors.
I don't think so, at least not as they are currently proposed.
But i guess both Features could be resolved with one General solution.
Apr 10, 2014 at 6:14 PM
Oops, it seems like I messed up my post, the end doesn't make any sense. Anyway, the 2 problems are almost identical so the solution would probably be identical or at least very similar. And yes, any solution would require CLR changes.
Dec 18, 2014 at 10:07 AM
Is there a way to vote for this feature? so developers could add it to the development stack of CLR / C#

imho constructor constraint on generic declaration is very useful feature, perhaps even more important than all that are introduced for now.
Dec 18, 2014 at 2:47 PM
Flowster wrote:
Yes, if reflection is needed for these constraints, otherwise the Compiler could Emit Activator.CreatInstance statements as needed and checking the constraints without Emiting these into signatures in the assembly.
AFAIK even new() constraint result in Activator.CreateInstace being emitted.
Dec 18, 2014 at 6:09 PM
stassultanov wrote:
imho constructor constraint on generic declaration is very useful feature, perhaps even more important than all that are introduced for now.
It would be a useful feature if it had been provided for from the outset; unfortunately, the while the design of the generics system in .NET is mostly quite good, it doesn't really contain any mechanisms for extending it. All but three constraints are inheritable (meaning that if they are satisfied by some type T, and type U is constrained to T, they will be satisfied by U). Static method and constructor constraints would not be inheritable, and adding those would make it necessary to go from having three non-inheritable constraints (each of which is simply present or not) to having an unbounded number of them. Further, for such constraints to be useful, it would be necessary that generic types with particular constraints be passable to generic type parameters which have those same constraints, and that any classes which satisfy the constraints be identifiable as doing so. I see no nice way of bootstrapping that process, though perhaps it is possible.
Dec 23, 2014 at 10:19 AM
Lets decompose problem on the steps

First step : Declaration
    class Gen<T> where T : Stream, new(string, FileMode)
    {
        public T Foo(String path, FileMode mode)
        {
            return new T(path, mode);
        }
    }

    sealed class Program
    {
        static void Main(string[] args)
        {
            var gen = new Gen<FileStream>();

            using (var x = gen.Foo("test.txt", FileMode.OpenOrCreate))
            {
                x.WriteByte(1);
            }
        }
    }
this step is obviously clear. We define 2 constraints to the T. It is derived from the Stream and must have a public constructor which accepts string and FileMode.

Second step : on compile time compiler see that FileStream must have a public constructor which accepts string and FileMode
it could be checked in this way:
            var constructor = typeof(FileStream).GetConstructor(new[]
            {
                typeof(string), typeof(FileMode)
            });

            if (constructor == null)
            {
                // Throw Compile Time Exception
            }
and if required constructor is found replace
return new T(path, mode);
to
return (FileStream) constructor.Invoke(path, mode);
Jan 12, 2015 at 6:19 PM
stassultanov wrote:
Second step : on compile time compiler see that FileStream must have a public constructor which accepts string and FileMode
it could be checked in this way:
What happens if someone wants to write a generic class GenHolder<T> which contains a member of type Gen<T>? The compiler wouldn't be able to get a Type object for T and call GetConstructor on it, since the type wouldn't be known (and might not even exist!) until run-time.
Jan 12, 2015 at 9:10 PM
supercat wrote:
stassultanov wrote:
Second step : on compile time compiler see that FileStream must have a public constructor which accepts string and FileMode
it could be checked in this way:
What happens if someone wants to write a generic class GenHolder<T> which contains a member of type Gen<T>? The compiler wouldn't be able to get a Type object for T and call GetConstructor on it, since the type wouldn't be known (and might not even exist!) until run-time.
There seems to be some confusion here regarding compile time and runtime. Obviously, "throw compile time exception" makes sense only if we're talking about the JIT compiler rather than the C# compiler. But that's still wrong because in the case of the JIT compilation the exception would be thrown much earlier, when the runtime attempts to create a Gen<X> type where X doesn't satisfy the constraint.
Jan 13, 2015 at 7:11 PM
mdanes wrote:
There seems to be some confusion here regarding compile time and runtime. Obviously, "throw compile time exception" makes sense only if we're talking about the JIT compiler rather than the C# compiler. But that's still wrong because in the case of the JIT compilation the exception would be thrown much earlier, when the runtime attempts to create a Gen<X> type where X doesn't satisfy the constraint.
The error could only be flagged at class creation if the constraints were encoded in the class definition, something which is not at present possible for the proposed generic-constructor constraints. It might be possible to establish a convention that classes which have a constructor which takes parameters of types e.g. int, double, ref String should automatically implement IIncludesPublicConstructor<int, double, RefParameter<String> [assuming the Framework defines a set of such interfaces along with a dummy type RefParameter<T> for use indicating ref parameters]; if a class implements such an interface without including the appropriate constructors, an attempt to use one of those constructors would fail at runtime, and unless the Runtime automatically regards classes with the right constructors as being implementations of those interfaces it would not be possible to use classes which have the proper constructors but were compiled before the convention was established. Even with those limitations, there may be some value in such a feature; not sure it would be seen as sufficient to justify the effort, however.
Jan 14, 2015 at 10:11 AM
supercat wrote:
stassultanov wrote:
Second step : on compile time compiler see that FileStream must have a public constructor which accepts string and FileMode
it could be checked in this way:
What happens if someone wants to write a generic class GenHolder<T> which contains a member of type Gen<T>? The compiler wouldn't be able to get a Type object for T and call GetConstructor on it, since the type wouldn't be known (and might not even exist!) until run-time.
on compile time compiler exactly knows all constraints of T and checks if instance satisfies them.
if you would try to compile something like this
    class Gen<T> where T : new()
    {
    }

    class GenHolder<U>
    {
        private Gen<U> test;
    }
compiler will tell you that U must have public parameter-less constructor.

Well i belive that the same must happen when compiler would try to compile this code
    class Gen<T> where T : new(string, FileMode)
    {
    }

    class GenHolder<U>
    {
        private Gen<U> test;
    }
compiler must say that U must have public constructor which accepts String and FileMode
Jan 14, 2015 at 10:31 AM
stassultanov wrote:
if you would try to compile something like this ... compiler will tell you that U must have public parameter-less constructor
Yes, but there are also cases where the error shows up only at runtime:
class Program {
    class Gen<T> where T : new() {
    }

    class Foo {
        public Foo(int x) {
        }
    }

    static void Main() {
        typeof(Gen<>).MakeGenericType(typeof(Foo));
    }
}

This throws an exception at runtime because Foo doesn't satisfy the new() constraint. Same thing needs to happen for a constraint such as new(string, FileMode) and this requires runtime support to be implemented properly.
Jan 14, 2015 at 10:38 AM
So, guys, if we take the following code
    class Gen<T> where T : new()
    {
        public T CreateInstance()
        {
            return new T();
        }
    }
and look at disasembly
.class private auto ansi beforefieldinit ConsoleApplication20.Gen`1<.ctor  T>
    extends [mscorlib]System.Object
we will notice that keyword __.ctor __actually creates a public parameter-less constructor constraint
so in order to achieve a goal compiler must be extended / rewrited to accept following constraints
.class private auto ansi beforefieldinit ConsoleApplication20.Gen`1<.ctor([mscorlib]System.String, [mscorlib]System.IO.FileMode)  T>
    extends [mscorlib]System.Object
Jan 14, 2015 at 12:26 PM
stassultanov wrote:
so in order to achieve a goal compiler must be extended / rewrited to accept following constraints
The compiler and the runtime.

The metadata format has to be updated to allow such constraints to be encoded. The C# compiler has to be updated to generate such constraints. The runtime has to be updated to read the constraints from metadata and enforce them.
Jan 14, 2015 at 4:45 PM
stassultanov wrote:
Well i belive that the same must happen when compiler would try to compile this code
The problem is that either one would have to make Gen<T> only be usable from code compiled by any compiler that understands the new constraints (e.g. by using a name which is slightly different from the normal one for a single-parameter generic), or else allow for the possibility of code written using a compiler that doesn't understand such constraints creating a class which doesn't abide by them. From a practical migration standard, accepting the latter weakness would probably be more practical than having classes which couldn't be used at all from within any language whose compiler wasn't updated, especially since the limitation would be "contagious" [e.g. if a class Moo<T> wanted to include a member of type Gen<T>, then it would need to have a constraint which would make Moo<T> unusable by old compilers].
Jan 15, 2015 at 8:53 AM
Indeed there is a lot of work to be done. But i strongly believe that it should be done to bring .NET to the new level, and to guarantee that it is on the edge of technology.
Jan 15, 2015 at 4:29 PM
Just my 2 cents, but I think static interfaces would be a much nicer solution to the problem than extending this constraint, since they would not just solve this problem, but also make generics work nicer with operator overloading. But what issues arise when trying to implement static interfaces: who knows, probably lots, and its a whole different discussion than the one in this thread.
Jan 15, 2015 at 6:03 PM
stassultanov wrote:
Indeed there is a lot of work to be done. But i strongly believe that it should be done to bring .NET to the new level, and to guarantee that it is on the edge of technology.
I would like to see a new framework and languages emerge which encapsulated all the things that were good about .NET while correcting its deficiencies. I would expect that such a framework could be designed so as to facilitate both porting of, and interoperation with, .NET code, but the .NET design, brilliant as it is, has some limitations which could not be overcome without creating a "wall" between the .NET and the next-generation universes.