1

My code requires a field of my class to be set but I want the field to be set only once. If the developer tries to reset it/change it, I would ideally like the compiler to tell us off instead of getting a run time error. Is this possible?

Code to help explain

internal class Message
    {
        private string title = string.Empty;

        public string Title
        {
            get { return title; }
            set 
            {
                if (string.IsNullOrEmpty(title))
                    title = value;
                else
                    throw new Exception("Title can only be set once!");
            }
        }
    }

As you can see, the above will throw an exception but this is a run time error. Although the example here is fairly trivial the concept of writing compiler error or warning messages could be very beneficial.

Dave
  • 8,163
  • 11
  • 67
  • 103
  • 4
    In what way is [`readonly`](http://msdn.microsoft.com/en-us/library/acdd6hb7.aspx) insufficient? – Jon Oct 29 '12 at 10:17
  • The program doesn't compile; if I make either "private readonly string title" or "public readonly string Title", I get the error message "The modifier 'readonly' is not valid for this item' – Dave Oct 29 '12 at 10:19
  • Then obviously there is some mistake. Please post the code that doesn't work. – Jon Oct 29 '12 at 10:20
  • That is the code! If I make 'private readonly string title = string.Empty" private (or if I change it to 'private readonly string title;') I get the message: "The modifier 'readonly' is not valid for this item". If I make the Public String Title readonly (public readonly string Title) then the message is "A readonly field cannot be assigned to (except in a constructor or a variable initializer)" – Dave Oct 29 '12 at 10:23
  • 1
    check this out for ideas and background: http://stackoverflow.com/questions/6266031/c-sharp-net-how-to-throw-a-compiler-error – Davide Piras Oct 29 '12 at 10:26
  • @DaveRook: I have no idea why `readonly` gives an error. It certainly works for me (see link in answer below), and what's more *this is exactly the use case for `readonly`*. – Jon Oct 29 '12 at 10:50

2 Answers2

4

What you are asking for is a custom compiler rule which AFAIK isn't possible. IMO you have 2 options, one is to make it part of the constructor so it can only ever be set once e.g.

internal class Message
{
    public Message(string title)
    {
        Title = title;
    }

    public string Title { get; private set; }
}

and the other is to keep it the way you have it, however, throwing a more appropriate exception e.g.

internal class Message
{
    private string title = string.Empty;

    public string Title
    {
        get { return title; }
        set 
        {
            if (string.IsNullOrEmpty(title))
                title = value;
            else
                throw new InvalidOperationException("Title can be set only once!");
        }
    }
}

Either way, the Title property is only ever going to get set once.

James
  • 80,725
  • 18
  • 167
  • 237
  • But would either of these alert the developer via a compile warning message? – Dave Oct 29 '12 at 10:24
  • @Dave the first one would - it is now not writeable, and must be set via the constructor – Marc Gravell Oct 29 '12 at 10:26
  • @MarcGravell thank you - and thank you James for the edit, this has pretty much answered it :) – Dave Oct 29 '12 at 10:26
  • 3
    For info, I think the second approach here is not ideal... silently refusing to change a property is not a good outcome (and doesn't do what the question asks); a strong +1 for the first approach, though – Marc Gravell Oct 29 '12 at 10:27
  • yeah, I agree (hence why original code threw an error when it was attempted) – Dave Oct 29 '12 at 10:27
  • @MarcGravell I agree about the silent refusal (I actually had that in mind as well). However, if the goal is purely just to avoid the property being changed more than once I thought it *could* be overlooked. On the flip side though, I could see how it could cause problems in the future (perhaps the `Exception` would be better in there for that reason alone). See update. – James Oct 29 '12 at 10:30
  • Actually, the goal was to display a compiler message but it doesn't matter :) – Dave Oct 29 '12 at 10:36
  • @DaveRook wasn't the point of the compiler error to point out that the developer was setting the property more than once though? Same goal, different solutions :) – James Oct 29 '12 at 10:38
  • @DaveRook you can't do it the other way - you'll get into the "halting problem" trying to figure out which code paths set it. – Marc Gravell Oct 29 '12 at 10:44
  • first option will show a compiler error if set outside the constructor. – InContext Oct 29 '12 at 13:56
1

Simply use a readonly field:

private readonly string title;

public Message(string title) { this.title = title; }

The compiler will emit an error if the field is assigned to from anywhere else.

Jon
  • 428,835
  • 81
  • 738
  • 806