21

I have two similar structs in C#, each one holds an integer, but the latter has get/set accessors implemented.

Why do I have to initialize the Y struct with new operator prior to assigning the a field? Is y still a value type when I init it with new?

public struct X
{
    public int a;
}

public struct Y
{
    public int a { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        X x;
        x.a = 1;

        Y y;
        y.a = 2; // << compile error "unused local variable" here

        Y y2 = new Y();
        y2.a = 3;
    }
}
8
  • 5
    I'm kind of surprised X x; x.a = 1; works... Commented Nov 26, 2013 at 19:46
  • 3
    Mutable structs are evil. I'm very familiar with C#, but I fully expected your example with X to not compile. It does, oddly enough. However, X x; Console.WriteLine(x); does not, and X x; x.a = 1; Console.WriteLine(x); does work. Commented Nov 26, 2013 at 19:47
  • Also, you shouldn't have non-private fields, you should make them properties (like in Y). Commented Nov 26, 2013 at 19:48
  • 5
    I think we need Jon Skeet or Eric Lippert to explain this magic. Looks like assigning all fields counts as initialization. But in second case it's method calling on uninitialized struct. Commented Nov 26, 2013 at 19:50
  • 2
    Here comes the specification! "12.3 Definite assignment" (page 122 of ecma-334). "A struct-type variable is considered definitely assigned if each of its instance variables is considered definitely assigned" Commented Nov 26, 2013 at 20:13

4 Answers 4

14

The reason one is valid while the other is not is that you cannot call methods on uninitialised objects. Property setters are methods too.

public struct X
{
    public int a;
    public void setA(int value)
    { this.a = value; }
}

public struct Y
{
    public int a { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        X x;
        x.setA(1); // A: error
        x.a = 2; // B: okay

        Y y;
        y.a = 3; // C: equivalent to A
    }
}

The reason that is not allowed is that the property setter could observe the uninitialised state of the object. The caller does not know whether the property setter merely sets a field, or does more than that.

Sign up to request clarification or add additional context in comments.

4 Comments

That's very strange. Would A be allowed after B? If so, what if X has another member variable? And if not, then what is the point in allowing members of an uninitialized struct to be set at all?
@BlueRaja-DannyPflughoeft Yes, A after B would be allowed, and no, not if X has another field. When all fields of a struct have been assigned to, the struct as a whole is considered initialised.
Really!? What if the struct member is set inside a method, or in a lambda? Does the compiler seriously track down which methods alter which struct-members? If not, what if the struct is a member-variable which is initialized in a constructor? This seems to have too many special cases to fit in with the C# design philosophy of "Keep is simple, stupid"
@BlueRaja-DannyPflughoeft If a struct is not fully initialised, you cannot pass it to another method (except with out), so no, the compiler does not need to track what other methods do. And class fields, even if they are of a struct type are always initialised, even if the class's constructor doesn't set them.
7

In first case you just assigning field. It doesn't involve actual using of structure, just setting value into memory (struct address + field offset on stack).

In second case you calling method set_a(int value), but fail because variable is uninitialized.

In third case constructor initializes it for you, so using variable is ok.

Update: Here comes the specification!

"12.3 Definite assignment" (page 122 of ecma-334).

A struct-type variable is considered definitely assigned if each of its instance variables is considered definitely assigned

Comments

5

This is covered in the C# specification section 5.3 dealing with "Definite Assignment":

a struct-type variable is considered definitely assigned if each of its instance variables is considered definitely assigned.

and:

An initially unassigned variable (Section 5.3.2) is considered definitely assigned at a given location if all possible execution paths leading to that location contain at least one of the following:
* A simple assignment (Section 7.13.1) in which the variable is the left operand.
* ...

As such, this also works:

void Main()
{
    X x;
    x.a = 1;
    x.b = 2;

    x.Dump();
}

public struct X
{
    public int a;
    public int b;
}

You can test this in LINQPad.

Note that there is no way for the compiler to prove that the struct-type variable is considered definitely assigned if you call code on it, and that's what you're doing with a property. As such, before you can use a property on the struct-type variable, it has to be definitely assigned.

Comments

-1

The new operator for value types runs the specified constructor. Unlike with reference types, this is optional, so if you don't use new, the default constructor is implicitly run (you cannot specify your own default constructor, so it always has the effect of giving the default value to the fields for their types).

As for why the compiler error, I'm not really sure. Interestingly, in the C# Interactive window,

public struct Y
{
    public int a { get; set; }
}
Y test;
test.a = 5;

works just fine.

2 Comments

This doesn't answer the question. Why is x.a = 1 allowed while y.a = 2 isn't?
"so if you don't use new, the default constructor doesn't get run." That's not true. It will be run implicitly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.