Skip to content

Proposal: Access to "this" in field initializers in C# #15610

@pmunin

Description

@pmunin

Problem: In many cases we developers still have to use constructor to initialize values, just because we don't have access to "this" in field initializers. Example of expected behavior:

public class MyGraph
{
   public class Node
   {
        public Node(MyGraph parent)
        { ...}
   }

   public Node Root {get;} = new Node(this);
}

This makes constructor a "semantic bottleneck" and trashes it up with some unrelated pieces of code that should be there near those fields declaration.

Foreseeing the question about the order of field initializers execution and how to know which fields of "this" are already initialized and can be used to see what to expect being initialized. Here is an example that demonstrates situation:

public class MyClass
{
     DbConnection connection = new DbConnection();
     string[] Names {get;} = this.connection.LoadNames();  //assumes "connection" to be initialized
}

I see 2 approaches:
a) Developer friendly way (Preferable): Execute initializers following the field declaration order. So if "connection" declared before "Names", then during "Names" initialization, connection is available. People can ask what to do in case of partial classes. I would suggest: in case of partial class initializers execution order matches the order of filenames, because that's how compiler will join those files.

//file1.cs
partial class MyClass
{
     string[] Names3 {get;} = this.connection.LoadNames();  //NullReference
}

//file2.cs
public partial class MyClass
{
     string[] Names {get;} = this.connection.LoadNames();  //NullReference
     DbConnection connection = new DbConnection();
     string[] Names2 {get;} = this.connection.LoadNames();  //Success
}

//file3.cs
partial class MyClass
{
     string[] Names4 {get;} = this.connection.LoadNames();  //Success
}

b) Simple, but hostile way: even though field initializers were executed and values calculated - compiler can just keep fields unassigned until all initializers executed, right before execution of constructor. This would be suckier than the option "a", but at least we'll have link to "this" assigned and can do the workaround using Lazy:

public class MyClass
{
     DbConnection connection = new DbConnection();
     Lazy<string> Names {get;} = new Lazy<string>(()=>this.connection.LoadNames());
}

Regardless of the approach, having any of those two options would be better than having none, and having to trash up the constructor.

This is definitely doable just on compiler level (no changes to CLR required) and it also does not break existing code, because now devs cannot use "this", all initializers are basically static methods code.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions