4

I have the following code (simplified), a structure and a class.

public struct pBook
{
    private int testID;

    public string request;
    public string response;
    public Int32 status;
    public int test_id
    {
        get
        {
            return testID;
        }
        set
        {
            testID = value;
        }
    }
};

public class TestClass
{
    public static void Main(string[] args)
    {
        pBook Book1;
        pBook Book2;

        Book1.request = "a";
        Book2.response = "b";
        Book2.status = 201;
        Book2.test_id = 0;  //this doesn't work, why?
    }
}

At the statement

Book2.test_id = 0;

I get the error

use of unassigned local variable 'Book2'

Any ideas how to correct?

Nick
  • 483
  • 1
  • 6
  • 15
  • The difference is that `test_id` is a property, so this is actually a method call while the other assignments are only accessing fields. But to me this feels inconsistent, the error should appear for the other assignments too or not at all. – René Vogt Dec 12 '19 at 14:32
  • You have it as private test_id.Private access is the least permissive access level. Private members are accessible only within the body of the class or the struct -> https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/private – panoskarajohn Dec 12 '19 at 14:33
  • 1
    I consider both "duplicate" questions to be badly chosen because they do not deal with the `struct` scenario. This duplicate does deal with structs: [Struct initialization and new operator](https://stackoverflow.com/q/20226477/1220550) – Peter B Dec 12 '19 at 14:40
  • 1
    @PeterB Understood and in retrospect, I agree. I apologize for the inconvenience. –  Dec 12 '19 at 15:52

1 Answers1

7

In "definite assignment", a struct requires all fields to be assigned before methods can be called, and properties (even property setters) are methods. A lazy fix is simply:

var Book2 = default(pBook);
// the rest unchanged

which fools definite assignment by explicitly setting everything to zero. However! IMO the real fix here is "no mutable structs". Mutable structs will hurt you. I would suggest:

var Book2 = new pBook("a", "b", 201, 0);

with (note: this uses recent C# syntax; for older C# compilers, you may need some tweaks):

public readonly struct Book
{
    public Book(string request, string response, int status, int testId)
    {
        Request = request;
        Response = response;
        Status = status;
        TestId = testId;
    }
    public string Request { get; }
    public string Response { get; }
    public int Status { get; }
    public int TestId { get; }
};
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • absolutely brilliant thank you (and thank you for the advice). So I keep the gets to be able to read, and as a 'set' I use the public Book(string... etc right? – Nick Dec 12 '19 at 14:42
  • @Nick: Correct. With an immutable struct you do not change an individual field. You replace the *entire* struct with a different one. The same way that when you add 3 to 2, you do not *change* 2 or 3 into 5. 2 and 3 stay 2 and 3; you produce a *new* value. Structs are *value types*, so you should treat them as *values*, not as mutable state. – Eric Lippert Dec 12 '19 at 17:21
  • If you do find yourself wanting to change individual fields on immutable structs, a common work-around is to add a fluent interface to do so. E.g., `book = book.WithStatus(5);` Alternatively, a generalized function can work (e.g., `book = book.With(Status:5);`). These sorts of functions, optionally implemented as extension methods, are just simple calls to the constructor. – Brian Dec 12 '19 at 19:47