2

As far as I understand, in the resharper suggestion " Implicitly captured closure: this", this refers to the instance of HomeController class. The question is now why did the following solutions worked to fix the problem and removed the Resharper suggestion.

public class HomeController : Controller
{
    private ExcelDocument _excelDoc = new ExcelDocument();
    private IDatabase _database = new CoreDatabase();

    [HttpPost]
    public JsonResult Insert(HttpPostedFileBase file, int programId)
    {
        _excelDoc.ReadData(firstDataRowIdx: 1, headerRowIdx: 0, excelFile: file);

        _excelDoc.CompareToDatabaseCodes(_database.
             .GetCodesWhere(c => c.ProgramID == programId && c.Active)// In this lambda Resharper says: 'Implicitly captured closure: this'
             .Select(rc => rc.Code)
             .ToList());

        _excelDoc.InsertTireIds(_database
             .GetTiersWhere(t => t.ProgramID == programId && _excelDoc.DistinctTiers.Contains(t.Tier)));

        //More code...
    }
}

I've found two ways to remove the Resharper suggestion without really understanding the reason behind those fixes.

  1. Assign programId parameter to local variable like this:

    [HttpPost]
    public JsonResult Insert(HttpPostedFileBase file, int programId)
    {
        _excelDoc.ReadData(firstDataRowIdx: 1, headerRowIdx: 0, excelFile: file);
    
        // Fix 
        int selectedProgramId = programId;
    
        _excelDoc.CompareToDatabaseCodes(_database.
             .GetCodesWhere(c => c.ProgramID == selectedProgramId && c.Active)// In this lambda Resharper says: 'Implicitly captured closure: this'
             .Select(rc => rc.Code)
             .ToList());
    
        _excelDoc.InsertTireIds(_database
             .GetTiersWhere(t => t.ProgramID == selectedProgramId && _excelDoc.DistinctTiers.Contains(t.Tier)));
        //More code...
    }
    
  2. Assign _excelDoc.DistinctTiers (in second lambda) to a local variable:

    [HttpPost]
    public JsonResult Insert(HttpPostedFileBase file, int programId)
    {
        _excelDoc.ReadData(firstDataRowIdx: 1, headerRowIdx: 0, excelFile: file); 
    
        _excelDoc.CompareToDatabaseCodes(_database.
             .GetCodesWhere(c => c.ProgramID == programId && c.Active)// In this lambda Resharper says: 'Implicitly captured closure: this'
             .Select(rc => rc.Code)
             .ToList());
    
        // Fix 
        List<int> distinctTiers = _excelDoc.DistinctTiers;
    
        _excelDoc.InsertTireIds(_database
             .GetTiersWhere(t => t.ProgramID == programId && distinctTiers.Contains(t.Tier)));
    
        //More code...
    }
    

Please help me understand why my fixes worked.

Let me know if more info is required to explain the problem better.

Thanks!!!

Nik
  • 77
  • 6
  • `this` refers to your class instance. Because lambda is accessing the `_excelDoc` *instance* field, it needs to capture the current instance. – Ivan Stoev Jan 05 '18 at 19:54
  • I understand what `this` refers to now. Howerver, it still doesn't make sence to me why both of my solutions to remove the suggestion worked. `_excelDoc` is still there :/ – Nik Jan 05 '18 at 20:18
  • @НикитаУрюпин The duplicate explains exactly what the error message is trying to warn you about, and why the exact fix that you're using addresses that problem. – Servy Jan 05 '18 at 20:27
  • @Servy I still don't get how assigning method parameter/class field to local variable is a fix. Also, how is it possible that two completely different approaches fix the same problem. I've read duplicate thread many times. – Nik Jan 05 '18 at 20:35
  • @НикитаУрюпин Assigning a local changes what is closed over, and as the warning is based on what you're closing over, it affects it. – Servy Jan 05 '18 at 21:06
  • @Servy Could you plese show an example based on given code of what C# code would look like with fix vs. without fix. after program gets compiled. – Nik Jan 09 '18 at 16:27
  • @НикитаУрюпин The answer does exactly that...I don't see a need to repeat it. If you want to see more examples, you can just look up the generated code for closures and find plenty more examples showing how the C# compiler implements closures, if you want to see more. – Servy Jan 09 '18 at 16:36

1 Answers1

0

What it means is that you're including a field from the class in a lamda expression. Because Resharper doesn't know what that lamda is going to be used for, it can't be certain that it isn't going to survive beyond the scope of the method call. If, for instance, that lamda is being assigned to some long-lived field of another class, it will refer to the current value of this._excelDoc - which might in the meantime have been changed, or set to null etc...

Resharper is simply telling you that the lamda's lifecycle shouldn't go beyond the scope of the method, or it will continue to operation on the _excelDoc as referred to by this instance.

Adam Brown
  • 1,667
  • 7
  • 9
  • this being the HomeController class instance - ok that makes scene. Still don't get how those two solutions fixed the issue for me. – Nik Jan 05 '18 at 20:14