16

According to this answer at stackoverflow, the generic type in C# is resolved at runtime.

However, according to this answer, in C#, the generic type is resolved at compile time.

What am I missing here?

In other words, is the type T resolved at compile time or run time?

Update:

Based on Oded's answer, In a case like this, where the type is a closed concrete type (which means it would be resolved at compile time)

class Program
{
    static void Main()
    {
        var t = new Test<int>();
    }  
}

public class Test<T>
{   
}

will the MSIL have the equivalent of

class Program
{
    static void Main()
    {
        var t = new Test();
    }
}

public class Test<int>
{        
}
Community
  • 1
  • 1
Foo
  • 4,206
  • 10
  • 39
  • 54
  • 3
    Those two posts you link to actually are describing different concepts. – JerKimball Jul 18 '13 at 21:31
  • 2
    What part of this from the posted answer in the link you provided do you not understand `No; that's fundamentally impossible. The whole point of generics is that they create compile-time types. You're trying to create a type which is unknown at compile time. You can do it using reflection, though. (typeof(MyClass<>).MakeGenericType(myType))` – MethodMan Jul 18 '13 at 21:31
  • I am guessing that you are looking for a `How to do Reflection` Example – MethodMan Jul 18 '13 at 21:32
  • What I am trying to ask is, is the Type T resolved at compile or run time? – Foo Jul 18 '13 at 21:35
  • 1
    Then you'll need to clearly define what it means for a type to be "resolved", because apparently at least two people are using it to mean at least two different things. I do not know what characterizes a "resolved" type. I know what characterizes *overload resolution*; is that what you mean by a type being "resolved"? – Eric Lippert Jul 18 '13 at 21:39
  • 4
    The answer to your updated question is *no*. The constructed generic type is not created until *runtime*. At compile time the compiler will determine that `int` is a legal argument corresponding to `T`, and that's all. Moreover: the body of `Test` must contain code that can be compiled given *any* possible value for `T`, not just the values that appear in `Main`. This is one of the main differences between generics and templates. – Eric Lippert Jul 18 '13 at 21:47
  • @EricLippert I smell a blog post topic... – JerKimball Jul 18 '13 at 21:49
  • @JerKimball: These might be of interest: http://blogs.msdn.com/b/ericlippert/archive/2007/06/14/calling-static-methods-on-type-parameters-is-illegal-part-one.aspx, and http://blogs.msdn.com/b/ericlippert/archive/2007/06/18/calling-static-methods-on-type-parameters-is-illegal-part-two.aspx and http://blogs.msdn.com/b/ericlippert/archive/2007/06/21/3445650.aspx. – Eric Lippert Jul 18 '13 at 23:22
  • 1
    Or [What's the difference, part one: Generics are not templates](http://blogs.msdn.com/b/ericlippert/archive/2009/07/30/generics-are-not-templates.aspx) – Brian Jul 18 '13 at 23:24
  • @EricLippert nice, I hadn't read these before - virtual +1 to you, as I've already upvoted your answer :) – JerKimball Jul 18 '13 at 23:44
  • this might be a similar problem - http://stackoverflow.com/questions/18684008/why-does-my-generic-t-is-not-resolved/18684054#18684054 – No Idea For Name Sep 08 '13 at 15:11

5 Answers5

23

The problem is that the question is not well-posed. Two people are claiming opposite things: that types are "resolved" at runtime and that types are "resolved" at compile time.

Since they are contradicting each other, they must both mean something different by "resolved".

I do not know what it means for a type to be "resolved". I do know however what overload resolution is. When asked to solve an overload resolution problem that does not involve dynamic, the C# compiler determines which overload to call at compile time, based on the compile time information about the generic type. So for example, if you have:

static void Main()
{
    var d = new D();
    var p = new P<D>();
    p.N(d);//Displays In class B
}


class B
{
    public void M()// Note, not virtual
    {
        Console.WriteLine("In class B");
    }
} 

class D : B
{
    public new void M()// new, not overload
    {
        Console.WriteLine("In class D");
    }
} 

class P<T> where T : B
{
    public  void N(T t)
    {
        t.M();
    }
}

N always calls B.M even if P<T> is instantiated as P<D>. Why? Because the overload resolution problem that determines what the meaning of t.M is must be solved when P<T>.N is compiled, and at that time, the best the compiler knows is that t must be B, so it chooses B.M.

If that's not what you mean by "resolved" then clarify the question.

Foo
  • 4,206
  • 10
  • 39
  • 54
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Does you example show overload resolution or binding? While binding requires overload resolution, the only two methods which with name `M` have identical signatures, so "overload resolution" would only have one candidate to work with. I think a better example for "overload resolution" might be `static bool Compare(T p1, T p2) where T:class { return p1 == p2;} ... Compare("5", 5.ToString())`. Since the compiler can't know that `T` will be of type `String`, it can't know that it should use `String`-vs-`String` overload of `==` rather than perform a reference comparison? – supercat Jul 19 '13 at 15:21
  • BTW, is the reference-comparison of `==` an overload, or is it some other kind of compiler magic? An overload of `==` which took parameters of type `Object` would allow any two types to be compared, but the C# `==` operator doesn't work like that. Its behavior seems quite different from anything that could be achieved via normal overload resolution. – supercat Jul 19 '13 at 15:27
  • @supercat: Good question. It actually does use normal operator overload resolution with some small modifications. First, expressions of the form `x==null`, `null!=x` and so on are handled specially. Then overload resolution happens normally. Then there is a post-resolution validation step. If overload resolution chooses the `object==object` operator and one or both operands are not of reference type then an error condition is triggered. (This brief sketch does not describe all the subtleties; see the spec for details.) – Eric Lippert Jul 19 '13 at 15:31
  • @EricLippert, seeing this answer makes me have doubts on my own answer to a related question ( http://stackoverflow.com/questions/17817979/where-t-class-generic-constraint-and-const-value-declaration/ ). and you certainly seem like someone who could weigh in on that question; maybe even provide the correct answer – nicholas Jul 26 '13 at 02:53
  • @Nicholas: I will be answering that question on the Coverity Development Testing blog's new feature "Ask the Bug Guys" next week. I'll post a link when I do. The short answer is: there is no good reason why that scenario should not work; it is a likely bug in either the compiler or the specification. – Eric Lippert Jul 26 '13 at 14:52
12

You are missing the concepts of open and closed generic types.

Essentially, a closed generic type is when you actually specify existing types on a generic parameter/s (or they are inferred by the compiler). For example:

Nullable<int> nulInt;

An open generic type is one where one or more generic type is to be determined during runtime (so, the Nullable<T> class is an example).

Community
  • 1
  • 1
Oded
  • 489,969
  • 99
  • 883
  • 1,009
1
  1. the first answer is about method parameters
  2. and the second is about generic type parameters

this is what you're missing.

more precisely: 1. C# is statically typed by default, so when passing parameters you'll get the best fitting type and method. (Also check out the answer about "dynamic" parameters.) 2. Setting a generic type parameter by the C# syntax is about static types. Setting it by reflection is about something else.

something else: "in .NET" each type has an initialization phase at its first usage at runtime. (see static fields and static constructor)

so: All types are initialized at runtime, but static types are used (or dynamic...) at compile-time that's when they need to be "resolved".

eivan
  • 5
  • 1
1

Open types (myclass<T>) do not exist run time. But unbound types can exist at run time (myclass<>). To resolve an unbound type at runtime , you need to use typeof operator.

In other words, unless typeof operator is used, generic types are closed at compile time.

developer747
  • 15,419
  • 26
  • 93
  • 147
0

The answer from your first link (generics at runtime) is incorrect. Generics in C# is compile time types, which code will be generated by compiler. But Just in Time (JIT) will set the concrete used type during the first run. Ref J.Richter, 4th edition, Generics, p. 315

Artem A
  • 2,154
  • 2
  • 23
  • 30