Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Who actually last decide what is the Generic Type?

I have this function

 public static T2 MyFunc<T1, T2>( T1 a, T1 b, T2 c)
        {
            return c;
        }     

I'm creating 2 Persons class instances:

 class Person
         {  }

            Person p = new Person();
            Person p2 = new Person();

I'm calling the function with :

 MyClass.MyFunc(p, p2, 5);

my Question is :

Who actually decide about the T1 type ? (p ? p2 ? )

Because if the left one is Apple so he checks that the second one is Also an apple

and if the second one is Orange - he should check that the first one is also an Orange.

enter image description here

It seems weird to ask it becuase at compile time they will fail if not the same.

Still - who decide about the type ?

And second - If i change it to dynamic - on runtime- who will decide what the T1 type should be ?

like image 203
Royi Namir Avatar asked Feb 05 '12 12:02

Royi Namir


People also ask

How do you identify a generic type?

To examine a generic type and its type parametersGet an instance of Type that represents the generic type. In the following code, the type is obtained using the C# typeof operator ( GetType in Visual Basic, typeid in Visual C++). See the Type class topic for other ways to get a Type object.

How do generic types work?

A generic type is a class or interface that is parameterized over types, meaning that a type can be assigned by performing generic type invocation, which will replace the generic type with the assigned concrete type.

At what point the generics type checking is done?

Generics provide the type checking at compile time . Finding bugs in compile-time can save time for debugging java program, because compile-time bugs are much easier to find and fix. If you use generics , you need not to perform the type casting explicitly.

In which way generic declares one or more type variables?

An interface is generic if it declares one or more type variables. These type variables are known as the type parameters of the interface. It defines one or more type variables that act as parameters. A generic interface declaration defines a set of types, one for each possible invocation of the type parameter section.


2 Answers

At a high level, method type inference works like this.

First we make a list of all the arguments -- the expressions you supply -- and their corresponding formal parameter type.

Let's look at a more interesting example than the one you give. Suppose we have

class Person {}
class Employee : Person {}
...
Person p = whatever;
Employee p2 = whatever;

and the same call. So we make the correspondences:

p  --> T1
p2 --> T1
5  --> T2

Then we make a list of what "bounds" are on each type parameter and whether they are "fixed". We have two type parameters, and we start with no upper, lower or exact bounds.

T1: (unfixed) upper { }  lower { }  exact { }
T2: (unfixed) upper { }  lower { }  exact { }

(Recall our recent discussion in another question about the relative sizes of types being based on whether or not a type was more or less restrictive; a type that is more restrictive is smaller than one that is less restrictive. Giraffe is smaller than Animal because more things are Animals than are Giraffes. The "upper" and "lower" bound sets are exactly that: the solution to the type inference problem for a given type parameter must be larger than or identical to every lower bound and smaller than or identical to every upper bound, and identical to every exact bound.)

Then we look at each argument and its corresponding type. (If the arguments are lambdas then we might have to figure out the order in which we look at arguments, but you don't have any lambdas here so let's ignore that detail.) For each argument we make an inference to the formal parameter type, and add the facts that we deduce about that inference to the bound set. So after looking at the first argument, we deduce the bounds:

T1: (unfixed) upper { }  lower { Person }  exact { }
T2: (unfixed) upper { }  lower { }  exact { }

After the second argument we deduce the bounds

T1: (unfixed) upper { }  lower { Person, Employee }  exact { }
T2: (unfixed) upper { }  lower { }  exact { }

After the third argument we deduce the bounds:

T1: (unfixed) upper { }  lower { Person, Employee }  exact { }
T2: (unfixed) upper { }  lower { int }  exact { }

After we have made as much progress as we can, we "fix" the bounds by finding the best type in the bounds set that satisfies every bound.

For T1, there are two types in the bounds set, Person and Employee. Is there one of them that satisfies every bound in the bounds set? Yes. Employee does not satisfy the Person bound because Employee is a smaller type than Person; Person is a lower bound -- it means no type smaller than Person is legal. Person does satisfy all the bounds: Person is identical to Person and is larger than Employee, so it satisfies both bounds. The best type in the bounds set that satisfies every bound is for T1 is Person and for T2 obviously it is int because there is only one type in the bounds set for T2. So we then fix the type parameters:

T1: (fixed) Person
T2: (fixed) int

Then we ask "do we have a fixed bound for every type parameter?" and the answer is "yes", so type inference succeeds.

If I change the first argument's type to dynamic then how is T1 inferred?

If any argument is dynamic then inference of T1 and T2 is deferred until runtime, at which point the semantic analyzer considers the most derived accessible runtime type of the value as the type for the lower bound supplied by the dynamic argument.


If this subject interest you and you want to learn more, there is a video of me explaining the C# 3 version of the algorithm here:

http://blogs.msdn.com/b/ericlippert/archive/2006/11/17/a-face-made-for-email-part-three.aspx

(C# 3 did not have upper bounds, only lower and exact bounds; other than that, the algorithms are pretty much the same.)

A number of articles I've written about type inference problems are here:

http://blogs.msdn.com/b/ericlippert/archive/tags/type+inference/

like image 186
Eric Lippert Avatar answered Nov 12 '22 05:11

Eric Lippert


The possibility to omit the types in the call

MyClass.MyFunc(p1, p2, 5);

is a syntax candy (unless you're using anonymous types), and it compiles exactly identically to

MyClass.MyFunc<Person, int>(p1, p2, 5);

The compiler deduces the values for T1 and T2 according to the types of the parameters a, b and c. If p1 and p2 are of incompatible types (see svick's answer, the compiler won't be able to deduce T1 and this will result in a compilation error.

like image 20
Ilya Kogan Avatar answered Nov 12 '22 07:11

Ilya Kogan