124

I have a class and when I try to use it in another class I receive the error below.

using System;
using System.Collections.Generic;
using System.Linq;

namespace MySite
{
    public class Reminders
    {
        public Dictionary<TimeSpan, string> TimeSpanText { get; set; }

        // We are setting the default values using the Costructor
        public Reminders()
        {
            TimeSpanText.Add(TimeSpan.Zero, "None");
            TimeSpanText.Add(new TimeSpan(0, 0, 5, 0), "5 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 0, 15, 0), "15 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 0, 30, 0), "30 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 1, 0, 0), "1 hour before");
            TimeSpanText.Add(new TimeSpan(0, 2, 0, 0), "2 hours before");
            TimeSpanText.Add(new TimeSpan(1, 0, 0, 0), "1 day before");
            TimeSpanText.Add(new TimeSpan(2, 0, 0, 0), "2 day before");
        }

    }
}

Using the class in another class

class SomeOtherClass
{  
    private Reminders reminder = new Reminders();
    // error happens on this line:
    private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 
    ....

Error (CS0236):

A field initializer cannot reference the nonstatic field, method, or property

Why does it happen and how to fix it?

GibboK
  • 71,848
  • 143
  • 435
  • 658

6 Answers6

170

This line:

private dynamic defaultReminder = 
                          reminder.TimeSpanText[TimeSpan.FromMinutes(15)];

You cannot use an instance variable to initialize another instance variable. Why? Because the compiler can rearrange these - there is no guarantee that reminder will be initialized before defaultReminder, so the above line might throw a NullReferenceException.

Instead, just use:

private dynamic defaultReminder = TimeSpan.FromMinutes(15);

Alternatively, set up the value in the constructor:

private dynamic defaultReminder;

public Reminders()
{
    defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 
}

There are more details about this compiler error on MSDN - Compiler Error CS0236.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • 3
    Java is more 'forgiving' for these kind of constructs. Don't know whether that is a good thing. http://stackoverflow.com/questions/1494735/initialization-order-of-static-fields-in-static-class – Wouter Schut Feb 03 '15 at 11:52
  • 44
    No, the compiler cannot rearrange the initializers. The C# Language Specification states, under the section "10.5.5.2 Instance field initialization", the following: __The variable initializers are executed in the textual order in which they appear in the class declaration.__ This is even repeated in "10.11.2 Instance variable initializers" where they say: __The variable initializers are executed in the textual order in which they appear in the class declaration.__ So your explanation is wrong. The order is fixed. The reason why it is disallowed is that the designers of C# wanted it that way. – Jeppe Stig Nielsen Apr 18 '16 at 20:16
  • 1
    (Only in the case of a `partial class` with "parts" in several files is the order of the field initializers unclear, but that goes for `static` fields as well!) – Jeppe Stig Nielsen Apr 18 '16 at 20:18
  • 1
    @WouterSchut The thread you link is not about Java?! It is about C# as well, however with `static` fields instead of instance fields. – Jeppe Stig Nielsen Apr 18 '16 at 20:33
  • 1
    @JeppeStigNielsen Because they wanted it that way is always a bad reason for making a language design choice. If it could work except for they decided not to let it work, then they are making developers work around their choice, which in some cases can take quite a bit of extra code, depending on what we need to do. – Andrew May 05 '17 at 13:11
  • 2
    @Andrew Not true at all, Many decisions are made to forbid bad practices. even though they can, theoretically, be implemented, some guarded by Warnings, and some are plain Errors. and i think that this is one of these cases... even though the standard says it's sequencial, even an experienced developer wouldn't say it with confident (without searching the standard). – Tomer W Nov 07 '17 at 18:42
  • 1
    It would be so convenient to have this feature. I hope the language designers change their minds. – rory.ap Jan 26 '18 at 17:12
  • `private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];` is a field initializer and executes first (before any field without an initializer is set to its default value and before the instance constructor are executed). Instance fields that have no initializer will only have a legal (default) value after all instance field initializers are completed. Therefore the compiler cannot allow this property (or field) to be used _before the class instance is fully constructed_ to guarantee deterministic behavior. – BionicCode Sep 02 '19 at 20:25
  • This is also the reason why `this` is not allowed in instance field initializers. _"A variable initializer for an instance field cannot reference the instance being created. Thus, it is a compile-time error to reference this in a variable initializer, as it is a compile-time error for a variable initializer to reference any instance member through a simple_name."_ The only type members that are guarateed to be initialized before instance field initializers are executed are class (static) field initializers and class (static) constructors and class methods. – BionicCode Sep 02 '19 at 20:36
  • That's why instance field initializers are only allowed to reference a class member (static member). [Microsoft Docs: Class declarations](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#field-initialization). It has nothing to do with rearrangements of the initialization order by the compiler. As mentioned by @JeppeStigNielsen this order is deterministic and guaranteed by compiler rules. – BionicCode Sep 02 '19 at 20:42
  • @Bio - this all really should be in an answer of it's own. – Oded Sep 02 '19 at 20:47
29

You need to put that code into the constructor of your class:

private Reminders reminder = new Reminders();
private dynamic defaultReminder;

public YourClass()
{
    defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];
}

The reason is that you can't use one instance variable to initialize another one using a field initializer.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
10

you can use like this

private dynamic defaultReminder => reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 
  • 11
    Welcome to Stack Overflow! While this code snippet may solve the question, including an [explanation](http://meta.stackexchange.com/q/114762/305455) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please also try not to crowd your code with explanatory comments, as this reduces the readability of both the code and the explanations! – jmattheis Aug 06 '16 at 19:46
  • 5
    It's using => instead of = thus making it a property. – Vincent Dec 24 '16 at 01:15
  • 10
    Be careful using this technique, since using `=>` doesn't set the actual value, but will execute the code each time `defaultReminder` is accessed. This may not be intended, and have a negative performance impact, or generate unwanted pressure for GC, etc. – Smilediver Apr 16 '20 at 18:22
8

private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; is a field initializer and executes first (before any field without an initializer is set to its default value and before the invoked instance constructor is executed). Instance fields that have no initializer will only have a legal (default) value after all instance field initializers are completed. Due to the initialization order, instance constructors are executed last, which is why the instance is not created yet the moment the initializers are executed. Therefore the compiler cannot allow any instance property (or field) to be referenced before the class instance is fully constructed. This is because any access to an instance variable like reminder implicitly references the instance (this) to tell the compiler the concrete memory location of the instance to use.

This is also the reason why this is not allowed in an instance field initializer.

A variable initializer for an instance field cannot reference the instance being created. Thus, it is a compile-time error to reference this in a variable initializer, as it is a compile-time error for a variable initializer to reference any instance member through a simple_name.

The only type members that are guaranteed to be initialized before instance field initializers are executed are class (static) field initializers and class (static) constructors and class methods. Since static members are instance independent, they can be referenced at any time:

class SomeOtherClass
{
  private static Reminders reminder = new Reminders();

  // This operation is allowed,
  // since the compiler can guarantee that the referenced class member is already initialized
  // when this instance field initializer executes
  private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];
}

That's why instance field initializers are only allowed to reference a class member (static member). This compiler initialization rules will ensure a deterministic type instantiation.

For more details I recommend this document: Microsoft Docs: Class declarations.

This means that an instance field that references another instance member to initialize its value, must be initialized from the instance constructor or the referenced member must be declared static.

BionicCode
  • 1
  • 4
  • 28
  • 44
1

i am totally surprised about the accepted answer here by the community which is totally wrong, accepted answer says:

Because the compiler can rearrange these

as "Jeppe Stig Nielsen" says in a comment of accepted answer:

The C# Language Specification states: The variable initializers are executed in the textual order in which they appear in the class declaration

which doesn't mean that C# is an interpreting language, but it also doesn't mean that C# can rearrange lines of code!

so finally why this error happens? assume below code:

public class MyClass
{
    public int i = 5;
    public int j = i;  // CS0236

    public MyClass()
    {
        // ...
    }
}

let me show you how C# compiler see this code:

public class MyClass
{
    public int i = 5;
    public int j = this.i;  // CS0236

    public MyClass()
    {
        // ...
    }
}

"this" keyword which appears in public int j = this.i; reference MyClass class, but MyClass doesn't exist yet, why? because classes start their existence when their constructors end their executions and in C# logic constructors are executed last , which means they will be executed after instance field/variable initializers, so in public int j = this.i; expression, this is referring to nothing!

-1

You should initialize in the constructor. so do this:

    class SomeOtherClass
    {  
        private Reminders reminder ;
        // error happens on this line:
        private dynamic defaultReminder ;

    public SomeOtherClass()
    {
remider = new Reminders();
    defaultReminder =reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 
      }
Hasan Shouman
  • 2,162
  • 1
  • 20
  • 26