Passing a method parameter using Task.Factory.StartNew
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
You can pass a method parameter to Task.Factory.StartNew either by capturing it in a lambda or by using the overload that accepts an object state value. Both work, but they are not equally readable or equally appropriate in modern .NET code.
The bigger point is that StartNew is not the default recommendation for simple asynchronous work anymore. In many cases, Task.Run is clearer unless you specifically need StartNew options or scheduler control.
The Most Readable Pattern: Use a Lambda
For straightforward parameter passing, a lambda is usually the cleanest approach.
The lambda closes over value, so the method receives the parameter naturally.
Use the State Overload When You Need It
StartNew also has an overload that accepts a state object.
This avoids creating a closure, but the downside is loss of type safety because the parameter arrives as object and usually requires a cast.
For most application code, the lambda version is easier to read and maintain.
Multiple Parameters
If the method takes several values, the lambda approach stays simple.
Trying to force multiple values through the single object state parameter usually makes the code less clear, not more.
Why Task.Run Is Often Better
If your only goal is to offload work to the thread pool, Task.Run is usually the more modern choice.
Task.Factory.StartNew exposes more options, but it also has more footguns around schedulers and async delegates. That is why many developers now use Task.Run for simple background work and reserve StartNew for advanced cases.
Be Careful with Async Delegates
One common trap is using StartNew with an async lambda and expecting it to behave like Task.Run.
Because StartNew with an async lambda produces a nested Task<Task>, you often need Unwrap(). This surprises many people. Task.Run handles this more naturally.
When StartNew Still Makes Sense
Task.Factory.StartNew still matters when you need:
- a custom task scheduler
- long-running task hints
- attached child-task behavior
- fine-grained creation options
If you do not need those features, prefer the simpler API.
Common Pitfalls
- Using the
object stateoverload everywhere and filling the code with fragile casts. - Reaching for
StartNewwhenTask.Runwould be simpler and clearer. - Forgetting that
StartNewwith anasyncdelegate returns a nested task. - Assuming parameter passing is the hard part when the real issue is task-scheduler behavior.
- Capturing mutable variables in closures without understanding when their values may change.
Summary
- The easiest way to pass parameters to
Task.Factory.StartNewis usually a lambda. - '
StartNewalso supports a state-object overload, but it is less type-safe.' - For multiple parameters, lambdas stay cleaner than packing values into
object. - '
Task.Runis often the better default for simple thread-pool work.' - Use
StartNewwhen you genuinely need its advanced scheduling or task-creation options.

