5

Why C# lambda expression can't use instance properties and fields, when is used in a class scope? See this example:

public class Point:INotifyPropertyChanged
{
    public float X {get; set;}
    public float Y {get; set;}

    PropertyChangedEventHandler onPointsPropertyChanged =  (_, e)  =>
                               {
                                   X = 5;
                                   Y = 5; //Trying to access instace properties, but a compilation error occurs
                               };
    ...
}

Why this is not allowed?

EDIT

If we can do:

public class Point:INotifyPropertyChanged
{
    public float X {get; set;}
    public float Y {get; set;}

    PropertyChangedEventHandler onPointsPropertyChanged; 
    public Point()
    {
        onPointsPropertyChanged =  (_, e)  =>
                               {
                                   X = 5;
                                   Y = 5;    
                               };
    }
    ...
}

Why we can't initialize onPointsPropertyChanged like a other fields inside the class scope?, for instancie: int a = 5. The field onPointsPropertyChanged always will be used after the constructor execute.

Delimitry
  • 2,987
  • 4
  • 30
  • 39
Raúl Otaño
  • 4,640
  • 3
  • 31
  • 65
  • The field is bound to this.X and this.Y when you call the initializer, so if they're not already available, it doesn't work. It doesn't matter that you aren't *using* onPointsPropertyChanged until after the constructor executes. – Tim Goodman Jun 10 '13 at 19:04

3 Answers3

8

You cannot access an object instance before its constructor runs (such as in a field initializer or base constructor call).

This is true both inside a lambda and outside a lambda.

C# < 4 had a bug that allowed this in certain cases.

Community
  • 1
  • 1
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • But for instance, inside a property you can access other properties and fields. It is not the same? You can assume the lambda scope is valid after the constructor run. – Raúl Otaño Jun 10 '13 at 18:32
  • 1
    @RaulOtaño A property is effectively a method - this is a *field*, so the field initializer rules apply. – Reed Copsey Jun 10 '13 at 18:33
6

A field initializer cannot reference the non-static field, method, or property ...

Field initializers are executed before the constructor is executed. You're not permitted to reference any fields or properties before the constructor is executed.

Change your initialization to set the lambda function in your classes contructor:

public class Point : INotifyPropertyChanged
{
  public float X { get; set; }
  public float Y { get; set; }

  PropertyChangedEventHandler onPointsPropertyChanged;

  public Point()
  {
    onPointsPropertyChanged = (_, e) =>
    {
      X = 5;
      Y = 5;
    };
  }
}
Matt Houser
  • 33,983
  • 6
  • 70
  • 88
  • `onPointsPropertyChanged` is a class field, then why can not be initialized when declared, like a normal field: int a =5 for instance? – Raúl Otaño Jun 10 '13 at 18:40
  • @RaulOtaño, It can be initialized when declared. But you can't access `this.X` and `this.Y` yet. The same would be true if you said `int a = [some non-static field or property]` – Tim Goodman Jun 10 '13 at 18:47
  • 1
    When the compiler is building it's expression tree, it needs a reference to `this` in order to reference `X` or `Y`. At the time of the initialization, the variable `this` does not exist yet. `this` does not exist outside the scope of an instance method (such as a non-static method or a constructor). – Matt Houser Jun 10 '13 at 18:51
  • @Matt Houser, It is a bit more clear now. I can assume is because it is a bit more easy to implement. For instance, the compiler would assume the scope of the lambda's body, would be the same of an instance method, instead of to create a static scope. – Raúl Otaño Jun 10 '13 at 19:04
  • @RaulOtaño, I suspect the motivation of this rule is less about ease of implementing the compiler than about not wanting you to access a partially-initialized class. What you are trying to do is access properties of a class that has not finished running all of its field initializers. That means the class could be in an inconsistent state. – Tim Goodman Jun 10 '13 at 19:11
  • Also, note that the rule is the same regardless of whether you're dealing with lambdas. Non-static properties of the class are never accessible from its field initializers. – Tim Goodman Jun 10 '13 at 19:12
-2

What instance are you referring to? At the point where you are creating the lambda expression, no instance has yet been created; so what instance would those calls to X and Y be bound to?

David Nelson
  • 3,666
  • 1
  • 21
  • 24
  • 1
    Actually, an instance *does* exist at that point. An instance has to have been created for the field he is assigning to to exist. – Servy Jun 10 '13 at 19:03
  • No, an instance is being created at that point. It does not yet exist, and cannot be referred to by any field initializers. – David Nelson Sep 05 '13 at 04:06
  • That you aren't allowed to refer to the object instance during its initialization doesn't mean it doesn't exist. The object is created, then it is initialized, and then it can be accessed. – Servy Sep 05 '13 at 04:46
  • Precisely. You cannot access the object until it has been initialized. Therefore, you cannot access it during initialization. – David Nelson Sep 10 '13 at 21:59
  • 1
    Just because you can't access it doesn't mean it doesn't exist. It exists, but just can't be accessed at that point in time. – Servy Sep 10 '13 at 23:03