5

I have three objects :

private static readonly Apple a, c;
private readonly Orange b;

This code is called from my constructor :

public SomeClass()
{
    a = new Apple();
    b = new Orange(a.getDna());
    c = new Apple(b.getDna());
}

It gives me the error Readonly field cannot be used as an assignment target. If I remove either the static or readonly modifiers, it compiles perfectly. (Is there a wrong warning here?)

On checking other answers here on SO, I found that I should use a static constructor like :

static SomeClass()
{
    a = new Apple();
    c = new Apple(b.getDna());
}

public SomeClass()
{
    b = new Orange(a.getDna());
}

But this would cause the static constructor to be called first and cause an error since b would not be initialized.

How do I circumvent this?

P.S. I'm relatively new to C#

Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
Hele
  • 1,558
  • 4
  • 23
  • 39
  • Usually Static one call first then call second one when you create new instance of the class and call that specific Constructor. But static constructor call when your application is initiated – Peyman Apr 21 '15 at 04:22
  • The static constructor will be called first, and it'll be called the first time you access the type. Thing is, why would you want to access an instance variable in a static context (`c = new Apple(b.getDna());`). That doesn't make sense (and it'll raise a compile time error) and smells from afar. To access `b` you need an *instance*, and you don't have one in the static context (there's no `this`). You should rethink your design and what you are aiming with all the `static/non-static` – Jcl Apr 21 '15 at 04:24
  • I've edited the question to show that the static constructor will be called first. About the second half of your comment, I will soon edit my question again with my actual code to show my I came to this problem. – Hele Apr 21 '15 at 04:28
  • I would strongly recommend you to change this implementation design as I see your static member(`c`) is dependent on instance member(`b`) which is not good idea at all. – Jenish Rabadiya Apr 21 '15 at 04:37

5 Answers5

9

Let's start by defining what is static and what's the difference between static and instance members.

A static member is a member that doesn't need an instance to exist: it "belongs to the class", and not to an object (a instance of the class).

Now the readonly modifier, says that a member can only be assigned a value in the constructor (or in its declaration, but that's not relevant here).

There are two types of constructors: static constructors and instance constructors... the difference is the same difference as above, and the readonly modifier is of course, applied to each type of constructor: static readonly would mean "you can only change its value in the static constructor", and instance readonly would mean "you can change its value in the instance constructor".

The static constructor is called the first time the type is accessed, so it's always called first.

Now, in the examples you are just randomly changing members to static or not just to try if it compiles.

Think about it for a second... in the static context you have no instance at all, so it's just not possible to access instance members on the static constructors... furthermore, by the time the static constructor is called, there's no way you could have any initialized instance, even externally defined, since it'll always be called before you have the chance to initialize one.

So this line within the static constructor makes no sense:

c = new Apple(b.getDna());

You are trying to access b, which is an instance member, but you are not saying which instance should you get the value from.

You should really rethink your design, and think why members would be static or not, not just "move things around and try to make it compile and run".

Jcl
  • 27,696
  • 5
  • 61
  • 92
  • It's incorrect to say that readonly, "says that a member can only be assigned a value in the constructor." It can also receive one upon declaration. – Shaun Luttin Apr 21 '15 at 04:52
  • @ShaunLuttin that's right, but I didn't see it relevant to the question: I'll edit and add it as a comment – Jcl Apr 21 '15 at 04:53
  • This may not be exactly relevant to the question, but anyway : My real code is that of a server. I have only one instance of the server class which spawns multiple mini-servers (basically, haha). Any of the mini-servers should be able to initiate a global shutdown in case of a major error. The main server class has a function to initiate a shutdown. To call this, I could either send a copy of the shutdown function to each of the mini-servers or make the server shutdown static, which I did since it was simpler. contd... – Hele Apr 21 '15 at 04:54
  • Now I realize that it only makes sense for my entire main server to be made static since only one copy of it will ever exist anyway. But if I were to make the main server an instance class, how would I solve this problem? – Hele Apr 21 '15 at 04:54
  • @Hele this wouldn't go in a comment and would require examples to think of a correct design... there are many many ways to achieve what you want – Jcl Apr 21 '15 at 04:57
1

The error message is in fact correct.

First, static means it belongs to the class. Non static means it is per instance. An instance method can modify a static variable, but a static method cannot modify an instance variable (which instance would it modify?)

Given that, readonly means you can only initialize during creation (e.g. constructor.)

You are getting the error because you are trying to assign a readonly static AFTER its creation in an instance constructor.

Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
hatcyl
  • 2,190
  • 2
  • 21
  • 24
1

The error is correct because if you create another instance of SomeClass the static field a will be assigned twice, against the readonly constraint.

Paolo Costa
  • 1,989
  • 1
  • 12
  • 15
1

You are trying to assign values to read only static variables inside a instance constructor. At the point you call the instance constructor that variables are already assigned a value, which is null.

public SomeClass()
{
    a = new Apple(); // it is already assigned as null. 
    b = new Orange(a.getDna()); //there is no value assigned to it yet
    c = new Apple(b.getDna()); //it is already assigned as null
}

This happens because the static constructor is called before the instance constructor. You can have more details here:

A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced.

But then, you have the problem when you are trying to access an instance variable inside the static constructor. At that point, the instance constructor is not yet called, which means your variable b is not yet initialized.

You are facing a very common problem, where you are trying to mix instance variables and static variables. This approach can lead to very strange behaviours, like the one you are facing.

I suggest to you to don't mix this variables, make them all static or or make them all instance, but don't mix it. Otherwise, you might face different problems in the near future

Mário Areias
  • 411
  • 4
  • 7
1

Is there a wrong warning here?

No. The warning is correct. If a field is readonly, we can assign it a value in two places: upon declaration or in the constructor. Further, if something is static, its associated constructor is static too. So, we can assign to a static and readonly field in two places:

  1. Upon declaration or
  2. in the static constructor.

We cannot do it in an instance constructor.

Your other question is about the inability of a static field to depend on an instance one.

How do I circumvent this?

Here is a creative way to circumvent it:

  1. In the static constructor, assign to static _b.
  2. In the instance constructor, assign the static _b to the instance's b.

We can even assign _b = null when we're done with it and still access the value that we earlier assigned to b.

Here is an example:

public class SomeClass
{
    private static readonly Apple a, c;
    private static Orange _b;
    private readonly Orange b;

    static SomeClass()
    {
        a = new Apple();        
        _b = new Orange(a.getDna());
        c = new Apple(_b.getDna());
    }

    public SomeClass()
    {
        b = _b;
        _b = null;
    }

    //
    // the rest is just class definitions
    //      

    public class Apple
    {
        public Apple(object o = null) {}
        public object getDna() { return new object(); }
    }

    public class Orange
    {
        public Orange(object o = null) { }
        public object getDna() { return new object(); }
    }
}

It does let you circumvent the problem.

Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467