5

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>.

brgerner
  • 4,287
  • 4
  • 23
  • 38
  • possible duplicate of [Why can't the C# constructor infer type?](http://stackoverflow.com/questions/3570167/why-cant-the-c-sharp-constructor-infer-type) – Florian Greinacher May 11 '12 at 07:48
  • 8
    @FlorianGreinacher: These aren't the same questions. – user541686 May 11 '12 at 07:49
  • 1
    Just a small point so not worth a whole answer... would `var x = new MyClass(5)` call the `MyClass(T parameter)` constructor on the non-generic class `MyClass`, or the `MyClass(T parameter)` constructor on the generic class `MyClass`? – Rawling May 11 '12 at 08:03
  • @Rawling Good remark. I think your comment is (in fact) worth to be an answer. – brgerner May 11 '12 at 08:08
  • 1
    I've a feeling this won't get an answer until @EricLippert notices it, and even then it'll just be the standard "it's such an unusual case that we felt our efforts would be better spent elsewhere"-style one. – Rawling May 11 '12 at 08:41
  • @Rawling I updated my answer strictly based on the C# specification. The answer can be confusing, but there are good reasons why this is not allowed. – David Anderson May 11 '12 at 09:55

5 Answers5

2

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).

user541686
  • 205,094
  • 128
  • 528
  • 886
2

Because a constructor is part of a class and so the type parameters probably belong on the whole type.

If you can choose a different type parameter to use when constructing an instance of a generic class then you are really constructing a different type of class. I know it's not the same as templated classes in C++ but the concept is similar.

To solve your particular issue you could use a 'templated' factory method.

Nick
  • 25,026
  • 7
  • 51
  • 83
2

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.

David Anderson
  • 13,558
  • 5
  • 50
  • 76
  • In the first class, the error is still on the first "constructor", not the second. And having an `implicit` or `explicit` conversion wouldn't make any difference. – Rawling May 11 '12 at 08:37
  • You cannot explicitly declare it, but `C()` is a valid constructor generated by the compiler, hence why the declarative syntax is `C c = new C();` and it conversion does make a difference. – David Anderson May 11 '12 at 09:06
  • @DavidAnderson Thanks for your impressions about internals. Do I understand it right that this feature would have two disadvantages: a) much work by .NET and C# team and b) because of a lack in current constructor syntax ? – brgerner May 11 '12 at 10:24
  • C# is a type-safe language, this would be a very *unsafe* feature for the langauge, so it would never be included. For example, in my last example with `String` and `Int32` breaks type-safety. I highly doubt they would ever include this to the language regardless of how much work it is. – David Anderson May 11 '12 at 10:30
  • Current constructor (calling) syntax is `new Type(Parameters)`. If syntax would be e.g. `new Type.New(Parameters)` we could add a constructor assigned type parameter, e.g. `new Type.New(Parameters)`. – brgerner May 11 '12 at 10:33
  • 1
    That's actually the `Factory Pattern` using a generic method, which is allowed. There isn't a need for any language changes to do that. – David Anderson May 11 '12 at 10:39
1

You can pass in a delegate instead of a strongly typed Func<T>. You will not be able to know the type at compile time, but you wouldn't have that when you pass in the Func<T> either. When you want that you'd have to make the whole class generic.

class Program {
        static void Main(string[] args) {
            Func<int> i = () => 10;
            var a1 = new ClassA(i);

            Func<string> s = () => "Hi there";
            var a2 = new ClassA(s);            
        }
    }

    internal class ClassA {
        private readonly Delegate _delegate;

        public ClassA(Delegate func) { // just pass in a delegate instead of Func<T>
            _delegate = func;
        }
    }
gjvdkamp
  • 9,929
  • 3
  • 38
  • 46
  • Yes this is possible. But here it is not possible to pass *func* as a *method group*. And I like to pass functions as *method groups* :-) – brgerner May 11 '12 at 08:22
  • ok.. and why exactly? What is the effect that you want to achieve? – gjvdkamp May 11 '12 at 08:35
  • I like *method group* syntax `new ClassA(Function1)` more than the counterpart `new ClassA((Func>) Function1)`. Granted, a blatant example. – brgerner May 11 '12 at 08:46
0

Because you can achive that by declaring your class as generic

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

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

then you will have implicitly a generic construct

anouar.bagari
  • 2,084
  • 19
  • 30