Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic types - Name can be simplified

I am using Visual Studio 2015 and I have below two statements.

Task<List<int>> taskWithInLineAction = new Task<List<int>>(() =>
{
    List<int> ints = new List<int>();
    for (int i = 0; i < 1000; i++)
    {
        ints.Add(i);
    }
    return ints;
});

Task<List<int>> taskWithFactoryAndState = Task.Factory.StartNew<List<int>>((stateObj) =>
{
    List<int> ints = new List<int>();
    for (int i = 0; i < (int)stateObj; i++)
    {
        ints.Add(i);
    }
    return ints;
}, 2000);

Vs2015 warns me that

Task.Factory.StartNew<List<int>>((stateObj)

can be simplified to

Task.Factory.StartNew((stateObj)

As I can see I can even use it without any type and VS infers generic type from lambda expression return value type, as below:

Task taskWithFactoryAndState = Task.Factory.StartNew((stateObj) =>
{
    List<int> ints = new List<int>();
    for (int i = 0; i < (int)stateObj; i++)
    {
        ints.Add(i);
    }
    return ints;
}, 2000);

But this is not applicable to the first statement. In this case, VS force me to include generic types.

Could you please help me, why these two behaves differently?

enter image description here

like image 846
Omer Avatar asked Oct 16 '16 10:10

Omer


2 Answers

The two code samples do different things. Easiest way to see this is to put a break-point in the function of both parts and to press F5. It will hit the break-point of the second part even though it is below the first.

Now for the explanation:

The first one is just creating a generic object and when calling the constructor you must explicitly say what type you are creating.

Task<List<int>> taskWithInLineAction = new Task<List<int>>(() =>
{
    List<int> ints = new List<int>();
    for (int i = 0; i < 1000; i++)
    {
        ints.Add(i);
    }
    return ints;
});

So when you write new ... you must specify what type.

However, in the second section you are executing a function which returns an object and you are assigning it to a variable.

Task<List<int>> taskWithFactoryAndState = Task.Factory.StartNew<List<int>>((stateObj) =>
{
    List<int> ints = new List<int>();
    for (int i = 0; i < (int)stateObj; i++)
    {
        ints.Add(i);
    }
    return ints;
}, 2000);

At this point the task has already been executed and is running in the background. Because you are not directly instantiating the object visual studio tells you that the return type can be inferred and therefore "name can be simplified".

If you look visual studio also says you can await it while it doesn't say that to the first section:

List<int> taskWithFactoryAndState = await Task.Factory.StartNew<List<int>>((stateObj) =>
{
    List<int> ints = new List<int>();
    for (int i = 0; i < (int)stateObj; i++)
    {
        ints.Add(i);
    }
    return ints;
}, 2000);
like image 171
Gilad Green Avatar answered Oct 10 '22 09:10

Gilad Green


Simple design perspective:

  • If an object is initialized using Generic Constructor, then type needs to be mandatory supplied and that's what flows to the parameters, which they need to strictly adhere
  • If an Object is initialized using Generic Method, then type may be defined using the method Output, and that needs to be adhered by the Type returned.

Thus the role reversal in the two cases defined above

Case 1: Using Generic Constructor

Initialization is done using Task class constructor, since we are using the generic version, therefore Task<T> constructor, check out the various options on the following link. Important point remains that Task<TResult> type is taking parameter as Func delegate with TResult as Output, where type of Output is mapped using the Task<TResult>, that's why its mandatory to define the type for Task class, during initialization, its not the other way round. It is not possible to use a Func delegate as parameter, whose return type is not TResult (by design), thus enforcing the strict type check. Constructor used in this case is following.

Task<TResult>(Func<TResult>)

Case 2: Using Generic Method

In this case Task.Factory.StartNew<TResult>(Func<TResult>) method is used to create a Task object, where the return type of the Task is managed by the Func delegate and its return type, thus the type checking is still enforced but its opposite to the first case, therefore Task returned should have the same type as the Func delegate, check the following link

In case of Visual studio its able to pick up the nuance details from the compiler, which it runs on every modification and can suggest, whether change is optional / mandatory and accordingly user is prompted for an action and thus graying out the optional code

like image 38
Mrinal Kamboj Avatar answered Oct 10 '22 09:10

Mrinal Kamboj