187

Why is type inference not supported for constructors the way it is for generic methods?

public class MyType<T>
{
   private readonly T field;
   public MyType(T value) { field = value; }
}

var obj = new MyType(42); // why can't type inference work out that I want a MyType<int>?

Though you could get around this with a factory class,

public class MyTypeFactory
{
   public static MyType<T> Create<T>(T value)
   {
      return new MyType<T>(value);
   }
}
var myObj = MyTypeFactory.Create(42);

Is there a practical or philosophical reason why the constructor can't support type inference?

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
theburningmonk
  • 15,701
  • 14
  • 61
  • 104
  • 2
    I had the same question two years before this one: http://stackoverflow.com/questions/45604, so technically this is a duplicate. Eric's answer is excellent and complete though. – Keith Jul 04 '13 at 09:09
  • If you are trying to pass multiple classes for a strongly typed view, try this: return View(Tuple.Create(new Person(), new Address())); – Marquinho Peli Feb 01 '16 at 17:08
  • This is the correct answer in my opinion. Since it is the only one that gives a pragmatic solution. A solution that can be used in real life. Using the factory pattern. Even better if you name your factory the same as your generic type. – Jonathan Alfaro Sep 07 '16 at 17:16
  • Please vote for the feature request! [*Proposal: Constructor type argument inference*](https://github.com/dotnet/csharplang/issues/281) – Colonel Panic Jun 14 '18 at 09:55

5 Answers5

145

Is there a philosophical reason why the constructor can't support type inference?

No. When you have

new Foo(bar)

then we could identify all types called Foo in scope regardless of generic arity, and then do overload resolution on each using a modified method type inference algorithm. We'd then have to create a 'betterness' algorithm that determines which of two applicable constructors in two types that have the same name but different generic arity is the better constructor. In order to maintain backwards compatibility a ctor on a non-generic type must always win.

Is there a practical reason why the constructor can't support type inference?

Yes. Even if the benefit of the feature outweighs its costs -- which are considerable -- that's not sufficient to have a feature implemented. Not only does the feature have to be a net win, it has to be a large net win compared to all the other possible features we could be investing in. It also has to be better than spending that time and effort on bug fixing, performance work, and other possible areas that we could put that effort. And ideally it has to fit in well to whatever the "theme" is of the release.

Furthermore, as you correctly note, you can get the benefits of this feature without actually having the feature itself, by using a factory pattern. The existence of easy workarounds makes it less likely that a feature will ever be implemented.

This feature has been on the list of possible features for a long time now. It's never been anywhere near high enough on the list to actually get implemented.

UPDATE March 2015

The proposed feature made it close enough to the top of the list for C# 6 to be specified and designed, but was then cut.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 34
    It's still a bit odd and inconsistent, IMO the language benefits of features being implemented consistently. But that's just my opinion. – Dykam Aug 25 '10 at 21:38
  • 1
    @Dykam. You are correct; consistency with other features is desirable, but it is far from the only desirable design consideration. Consistency is nice to have; we try to avoid unnecessary inconsistencies. But consistency with other features is very low on the list compared with other design criteria such as cost. – Eric Lippert Aug 25 '10 at 22:17
  • 2
    So the generic type inference feature exists in C# 4.0... but not on constructors? I guess this explains why the compiler is complaining about my constructor call to my TempIDTable generic class when I thought it would infer T based on the type I was passing. The constructor parameter is List, and I thought passing a List to the call would allow it to infer T's type, but instead I get "Using the generic type 'TempIDTable' requires 1 type arguments'. It just seems so straightforward that I'm surprised it won't infer the type. – Triynko Mar 14 '12 at 20:07
  • 2
    This missing feature makes it difficult to convert code to use generics, because I have many calls to this constructor like "using(TempIDTable table = new TempIDTable( sql_connection, typed_list_of_ints_or_longs_etc))... I'd have to manually go back through, identify the type of the list and explicitly set the generic type parameter. The alternative of using a factory pattern would also require changes, so that's not going to help. I would be really nice if a generic type could be used so transparently that I never even have to specify the type outside of the parameter values I pass to it. – Triynko Mar 14 '12 at 20:12
  • 1
    @Triynko: I agree absolutely, that would be *really nice*. Unfortunately, *really nice* is *nowhere near good enough* to make it to the top of the list of possible features. We have a list of "really nice" features literally longer than your arm; we get to do maybe one or two of them in a given release. – Eric Lippert Mar 14 '12 at 20:17
  • 3
    It seems like the feature is already there and working in some fashion according to this post: http://stackoverflow.com/a/45728/88409. Personally, I'd rather have a complete, consistent, and well-defined feature (type inference... such that you don't, ever, in any case, need to write the type name if it can be inferred from the argument type) or none at all, than to have a half-implemented feature that works in some cases but not others, simply because of the confusion it causes, getting my hopes up, being surprised, spending hours looking for answers, explanations, philosophies, etc. – Triynko Mar 14 '12 at 20:32
  • 3
    @Triynko: I'd like that too. However, the goal "the compiler makes all possible inferences of omitted types" is explicitly a non-goal of the C# language. There are enormous costs to achieving that goal; those resources can be better spent adding user value in other ways. Perhaps you would feel better if you programmed in F#? It has considerably more advanced type inference features than C#. – Eric Lippert Mar 14 '12 at 21:09
  • I'm not going to switch languages, and I don't know why you're saying it's a non-goal of C#, because as far as I can see the C# compiler already does type inference in virtually all scenarios EXCEPT the constructor case. The compiler appears to already "make all possible inferences of omitted types" and does a good job. I mean, I haven't run into a situation where I would expect it to infer a type and it couldn't, except this constructor. It just seems like something was skipped, and it would be easy to fix, but perhaps that's not the case. I'm not complaining, just trying to clarify. – Triynko Mar 16 '12 at 21:42
  • 11
    @Triynko: Oh good heavens no, the C# compiler fails to make all sorts of possible inferences. For example, `class B { public virtual void M() where U : T {} } class D : B { public override void M() {} } ... (new D()).M();` -- you and I can use our mental type inference to know for darn sure that the only possible type argument is int, but the C# compiler misses that trick. I could list examples all day; our type inference is quite weak compared to F#. – Eric Lippert Mar 16 '12 at 21:54
  • 5
    @Triynko: Or `short M(out T y){...} ... var x = M(out x);` You and I can reason that the only possible return type is short, and therefore x must be short, and therefore T must be short. But the C# compiler falls over *immediately* if there is *any* cycle in an implicitly typed local. – Eric Lippert Mar 16 '12 at 21:59
  • 6
    Or `void M(T t1, T t2){} ... M(new Giraffe(), new Turtle());` You an I can reason that the writer of the code probably intended T to be Animal, but the C# compiler does not reason "Giraffe and Turtle share a common base type Animal, therefore it is the best candidate for T". – Eric Lippert Mar 16 '12 at 22:03
  • 16
    Language consistency is a priority, but it is a low priority compared to more pragmatic concerns. And I assure you that we have a list literally longer than your arm of features that take one sentence to describe; had we twenty times the current budget we still would not want to implement everything on that list! You would not want to use a language where every possible feature that could be added was added, believe me. We choose how to spend our limited effort extremely carefully to provide the maximum value for customers. – Eric Lippert Nov 19 '12 at 17:01
  • Interestingly, a couple years later, this is now in the works for Roslyn. – Magus Apr 16 '14 at 20:18
  • @Magus: Well what has happened is (1) Roslyn's improved architecture has made it a lot easier for the development team to rapidly prototype and test these sorts of smaller features, and (2) the team is taking this opportunity to implement many small features for C# 6 rather than one big headliner feature as was done for C# 2, 3, 4 and 5. So lots of things on that long list are now being addressed. – Eric Lippert Apr 16 '14 at 20:26
  • Oh, I totally understand, I just think it's cool. Plus, now people from the future may know that things eventually changed. – Magus Apr 16 '14 at 20:55
  • 1
    What about being explicit with the arity? There's already a similar syntax for `typeof()`, so it could be `new Foo<,>("a", 1)`. – Guillaume86 Jun 10 '15 at 20:13
  • 3
    Ridiculous that this was cut from C# 6. The work-around is making a static non-generic version of the class with the same name, with a generic `Create` method that supports type inference to construct the generic version of the class. Annoying, redundant code that could be eliminated with this feature. Way more useful than a lot of the other features that were not cut. – Triynko Sep 19 '16 at 16:17
  • 2
    @Triynko: Feel free to pass that critique on to the designers of C#; that would likely be a more productive use of your time than putting a comment on a *six year old question* written by someone who hasn't worked on the C# team for four years. You can find the C# designers on their github project forum. – Eric Lippert Sep 19 '16 at 16:27
  • The answer and then the comments continually return to a cost vs. benefit discussion, but from what I comprehend in all of this, there is no clear explanation about how a constructor differs from any other allowed type-inference situation. That is really the point of the question. Isn't there always a need for a 'betterness' algorithm since base classes and same-name ambiguities also exist in those other situations, don't they? It is still unclear why the constructor situation weighed differently in the analysis. Simply stating the argument over again doesn't make it clearer. – C Perkins Nov 18 '20 at 03:13
  • 1
    @CPerkins: I deleted an earlier comment because it did not address your point clearly enough. The germane difference between ctors and regular methods is: with a regular method we resolve the method name in the context of first deducing its containing type and determining the group of methods with that name. We then infer generic type arguments from actual arguments. But with a constructor, there is no *name* at all; the name of the constructor and the name of its containing type are the same. – Eric Lippert Nov 18 '20 at 19:25
  • 1
    @CPerkins: That's the oddity that makes the feature require *work*. Now, as I pointed out in the answer and these comments, it's not *theoretically hard* work; there's no theory or philosophy objection. Rather, it's just plain, everyday language design scutwork of coming up with a new resolution algorithm that is similar enough to the existing algorithms for ordinary methods to seem familiar, but without introducing weird gotchas or breaking changes. That work has costs, and they're pretty large compared to the benefit. – Eric Lippert Nov 18 '20 at 19:27
16
public class MyType<T> 
{ 
   private readonly T field; 
   public MyType(T value) { field = value; } 
} 

they can, there is no need to tell the constructor 'what T is' again, seeing as you have already done that in the class decleration.

also your factory is incorrect, you need to have public class MyTypeFactory<T> not just public class MyTypeFactory - unless you declare the factory inside the MyType class

Edit for update:

Well, is 42 a long, a short, an int, or something else?

Let's say you have the following

class Base
{
   public virtual void DoStuff() { Console.WriteLine("Base"); }
}

class Foo : Base
{
   public override void DoStuff() { Console.WriteLine("Foo");  }
}

Then you did this

var c = new Foo();

var myType = new MyType(c);

Would you expect foo to be used, or base? We need to tell the compiler what to use in place of T

When you really wanted to on type base

Hence the

var myType = new MyType<Base>(c);
PostMan
  • 6,899
  • 1
  • 43
  • 51
  • good point, I meant to say why can't the compiler infer the type using the constructor, updating my question. the factory doesn't have to be generic though – theburningmonk Aug 25 '10 at 21:10
  • See my edit, if the factory is created inside the MyType Class it knows what T is, but if it's not, you will need T – PostMan Aug 25 '10 at 21:14
  • 2
    The compiler can not infer the type because there could me multiple constructors. To augment your example, how would the compiler know which constructor to call when `MyType(double)` is present? – Steve Guidi Aug 25 '10 at 21:14
  • 2
    @PostMan - but in the Create method the compiler can simply infer that T is int – theburningmonk Aug 25 '10 at 21:17
  • @theburningmonk it can't if the MyTypeFactory is not declared inside MyType class 'Cannot resolve symbol 'T'' – PostMan Aug 25 '10 at 21:20
  • You certainly do not want to make the factory generic, as that would prevent type inference. – Ben Voigt Dec 05 '10 at 21:46
  • @Ben Voigt - I didn't say so, the op originally had an error in his factory, I simply corrected it – PostMan Dec 06 '10 at 01:53
  • 4
    @PostMan: The part of your answer I'm disagreeing with is your factory is incorrect, you need to have `public class MyTypeFactory` not just `public class MyTypeFactory` - unless you declare the factory inside the `MyType` class Actually, the correct thing is `MyTypeFactory.Create` as the question now has, and not `MyTypeFactory.Create` which you said was needed. Using `MyTypeFactory.Create` would prevent type inference, since the generic parameter appearing before the member reference operator `.` cannot be inferred, the one on a method can. – Ben Voigt Dec 06 '10 at 02:03
  • @PostMan "Would you expect foo to be used, or base?" Unless specified by the user otherwise, I'd expect the most derived type to be preferred when attempting to infer the type. In the case of Eric Lippert's examples, I'd assume the type of the common ancestor would result from the inference. There is nothing preventing a compiler from being just as smart about type inference as we are. – Tom Lint May 14 '19 at 14:43
12

The main reason generic type inference can't work on constructors like you wish is because the class "MyType" doesn't even exist when all you've declared is "MyType<T>". Remember it is legal to have both:

public class MyType<T> {
}

and

public class MyType {
}

Both would be legal. How would you disambiguate your syntax if you did in fact declare both, and both of them declared a conflicting constructor.

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
  • "How would you disambiguate your syntax". In this case, you would have to specify T. – Pedro Henrique Sep 29 '17 at 17:05
  • But in the case of the question (and in my case) there is only a single class (that's generic). There is no ambiguity. If I have `var myVariable = new MyClass()`, how does the compiler know not to make myVariable of type Object or a base class of MyClass? All of the contrary cases presented in these answers don't really explain why it can't infer the type from the local code around a constructor... then if it gets it wrong, the coder makes it more explicit... just like in any other case where the type is inferred. It is always possible to override the default choice if necessary. – C Perkins Nov 18 '20 at 04:10
  • @CPerkins sure, and then if you ever create a non-generic version, all consumers using your suggested syntax would now have compiler errors. – Kirk Woll Nov 18 '20 at 14:06
  • 2
    @KirkWoll Sometimes it is valid to say "If you do this, then something bad might happen, so it's not allowed", but that type of reasoning doesn't stand especially when there are so many other situations where changes in a class or method declaration will break something. Take basic overrides. It's possible to declare an additional override that creates ambiguity and breaks existing code. So what? It requires refactoring or some alternative solution, not a decision beforehand to disallow overriding methods in the first place. – C Perkins Nov 18 '20 at 15:39
0

Although this has been answered many times before, I feel I need to clarify one thing: C# supports generic type inference on constructors. The problem is, it doesn't support neither adding generic parameters to constructors nor type generic type inference. Wanting to infer generic type argument of the type itself is basically the same as requiring Foo.Bar(0) to infer to Foo<int>.Bar(0).

IS4
  • 11,945
  • 2
  • 47
  • 86
0

The constructor needs to have the same generic specification as the class itself. Otherwise it would be impossible to know if the int in your example would relate to the class or to the constructor.

var obj = new MyType<int>(42);

Would that be class MyType<T> with constructor MyType(int) or class MyType with constructor MyType<T>(T)?

Albin Sunnanbo
  • 46,430
  • 8
  • 69
  • 108
  • 2
    This ambiguity can appear also with normal generic methods. – Dykam Aug 25 '10 at 21:15
  • 7
    It would not be *impossible* to know in most cases. In cases where it could not be unambiguously determined which was intended then we could give an error. This is the same as any other overload resolution problem; if insufficient information is supplied then we simply give an error. – Eric Lippert Aug 25 '10 at 21:32