Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are type parameters not allowed in constructors?

Tags:

c#

There are type parameters for methods. Why there are no type parameters for constructors?

Example

I think there are several (not many) examples where it would be usefull. My current problem was following:

internal class ClassA
{
   private readonly Delegate _delegate;

   public ClassA<T>(Func<T> func)
   {
     _delegate = func;
   }
}

A Delegate is enough for my class. But to pass it as method group I need to define the parameter as a Func<T>.

like image 584
brgerner Avatar asked May 11 '12 07:05

brgerner


People also ask

Can we pass parameters to constructor?

You can use any data type for a parameter of a method or a constructor. This includes primitive data types, such as doubles, floats, and integers, as you saw in the computePayment method, and reference data types, such as objects and arrays.

Can a constructor have a parameter list?

In this example, we have created the constructor of Student class that have two parameters. We can have any number of parameters in the constructor.

Which of the following Cannot be used as constructors?

Abstract class cannot have a constructor. Explanation: No instance can be created of abstract class.

How do you fix constructor Cannot be applied to given types?

Another way to fix the code is to define a constructor in the SpecificThing class that takes an argument and calls the superclass constructor, and then change the code in main to use that constructor instead, as shown below.


2 Answers

After reading the C# specification it actually does make sense, but it can be confusing.

Each class has an associated instance type, and for a generic class declaration the instance type is formed by creating a constructed type from the type declaration, with each of the supplied type arguments being the corresponding type parameter.

class C<T>
{ 
}

C<T> is a constructed type, and an instance type will be created by the process of constructing the type using the type parameters.

C<String> c = new C<String>();

The compiler took the constructed type C<T>, and using the supplied type parameters an instance type of C<String> was created. Generics is only a compile time construct, everything is executed in the terms of closed constructed types at runtime.

Now let's take your question and put it to the test here.

class C
{
    public C<T>() 
    {
    }
}

This isn't possible, because you are trying to construct a type that doesn't exist.

C c = new C<String>();

What is the implicit or explicit conversion between C and C<String>? There is none. It doesn't even make sense.

Because C is a non-generic type in this example, the instance type is the class declaration itself. So how do you expect C<String> to construct C?

The proper declaration for what you want to do is this.

internal class Class<T> 
{
    private readonly Delegate _delegate;

    public Class(Func<T> function) 
    {
        _delegate = function;
    }
}

Here because we have a constructed type Class<T>, the proper instance type can be created by the compiler.

Func<String> function = new Func<String>(() => { return String.Empty; });
Class<String> c = new Class<String>(function);

If you tried to do it the way you want in your question.

Func<String> function = new Func<String>(() => { return String.Empty; });
Class c = new Class<String>(function);

The constructed type would be Class<String>, which is not the same as type C, and there is no implicit or explicit conversion from either one. If this was allowed by the compiler, C would be in some unknown and unusable state.

What you do need to know about constructed types, is this.

class C<T>
{
    public C<T>() 
    {
    }
}

While you cannot explicitly declare a generic constructor, it is valid but only as a closed type constructor at runtime.

C<String> c = new C<String>();

At compile time, the following constructor is created.

public C<String>() 
{
}

Which is why this is valid:

C<String> c = new C<String>(); // We just used the closed type constructor

If what you wanted was allowed, something like this could happen.

class C<T>
{
    public C<U>() 
    {
    }
}

// ???
C<String> c = new C<Int32>();

You can see the problems now that would arise if the construct was allowed. Hopefully this sheds some insight, the specification is rather long and there are many many many sections that cover generics, type parameters, constructed types, closed and open types, bound and unbound.

It can get very confusing, but its a good thing that this isn't allowed by the compiler rules.

like image 130
David Anderson Avatar answered Sep 25 '22 06:09

David Anderson


They simply didn't include the feature, probably on the assumption that it wouldn't be very useful.

If you need it for the constructor, you probably need it for the whole type. But in the cases you don't, you can probably use Object anyway.

And if you can't... yeah, I don't think there's much of a way around it, sorry. :\ You can use reflection but of course that'd be ridiculously slow... or you could play tricks with generating methods dynamically, which might be worth it depending on your use case but which might also be slow (it adds at least 1 extra indirect call).

like image 39
user541686 Avatar answered Sep 23 '22 06:09

user541686