2

I was reading here about creating immutable classes in C#. It was suggested I make my classes this way for real immutability:

private readonly int id;

public Person(int id) {

    this.id = id;
}

public int Id {
    get { return id; }
}

I understand that, but what if I wanted to do some error checking, how can I throw an exception? Given the following class, I can only think to do it this way:

private readonly int id;
private readonly string firstName;
private readonly string lastName;

public Person(int id,string firstName, string lastName)
{
    this.id=id = this.checkInt(id, "ID");
    this.firstName = this.checkString(firstName, "First Name");
    this.lastName = this.checkString(lastName,"Last Name");

}

private string checkString(string parameter,string name){

    if(String.IsNullOrWhiteSpace(parameter)){
        throw new ArgumentNullException("Must Include " + name);
    }

    return parameter;

}

private int checkInt(int parameter, string name)
{
    if(parameter < 0){

        throw new ArgumentOutOfRangeException("Must Include " + name);
    }
    return parameter;   
}

Is that the correct way of doing it? If not, how would I accomplish throwing exceptions in immutable classes?

Community
  • 1
  • 1
  • this would be valid, as long as the client of is not able to modify your properties by direct access to fields. but more ideal way is declaring the properties with setter private. – kuhajeyan Jul 09 '15 at 09:30
  • 2
    @kbird - I'm aware of that, but in the link I provided I was told it wasn't really immutable, cause within the class you could still call the private setter. –  Jul 09 '15 at 09:31
  • 1
    that would be wrong concept what immutable means, immutability means client class ( a class outside ) should be able to modify your properties . (not the inside the class itself) – kuhajeyan Jul 09 '15 at 09:33
  • AH! So, a private setter is more acceptable practice, correct? –  Jul 09 '15 at 09:34
  • That's fine to do according to the construction design guidelines - https://msdn.microsoft.com/en-us/library/ms229060%28v=vs.110%29.aspx. You should also make sure you override `GetHashCode` and `Equals` to get the full value of immutable types. – Enigmativity Jul 09 '15 at 09:36
  • @Nexusfactor - No, a private setter is NOT acceptable. You should, as per my previous comment, override `GetHashCode` and `Equals`, and `GetHashCode` relies on the hash code NOT changing. – Enigmativity Jul 09 '15 at 09:37
  • @Enigmativity - So the private method I use to check my variables is okay, correct? It's not bad practice? –  Jul 09 '15 at 09:41
  • I would inline these two private methods and it will be totally okay. Also, there are no reasons to override GetHashCode & Equals – omikad Jul 09 '15 at 09:43
  • @omikad - What do you mean inline? Could you provide an example? –  Jul 09 '15 at 09:44
  • @omikad - Of course you would override `GetHashCode` and `Equals` - you would then be able to use the class in LINQ queries and dictionaries. – Enigmativity Jul 09 '15 at 09:51
  • @Enigmativity question contains no information about queries and dictionaries. So, you are right, it is good to have these methods, but I think it is something premature – omikad Jul 09 '15 at 09:54
  • 2
    @Enigmativity - immutable just means that an object cannot be altered (after creation). `GetHashCode` and `Equals` rely on immutable properties, but not the other way round. You can have immutable properties or classes without ever needing to (explicitly or implicitly) call any of those two methods. – Corak Jul 09 '15 at 09:58
  • 1
    @kbird - A private setter is a worse choice than a readonly backing field since a client class could always use the private setter via reflection – Bill Tür stands with Ukraine Jul 09 '15 at 09:59
  • 1
    @ThomasSchremser: http://stackoverflow.com/questions/934930/can-i-change-a-private-readonly-field-in-c-sharp-using-reflection is of interest here. A client class could change the private readonly field via reflection too. Reflection does most things (TM). – Chris Jul 09 '15 at 10:01
  • 1
    @Chris - yes, there is no "true" readonly... burning it to CD might do it... but still a readonly backing field is "better" than a private setter in the ways that it won't let you *accidentally* change the value in the class yourself. – Corak Jul 09 '15 at 10:05
  • @Corak: Yeah, I'd still prefer readonly for that reason (and I commented that on "F5 F5 F5"'s answer). So I agree a private setter is a worse choice but not for the reflection reason. :) – Chris Jul 09 '15 at 10:06
  • @ThomasSchremser yes, in that case you are correct. – kuhajeyan Jul 09 '15 at 10:09
  • updated my answer with generic method – Fabjan Jul 09 '15 at 10:28

4 Answers4

3

I would inline these two private methods:

  private readonly int id;
  private readonly string firstName;
  private readonly string lastName;

  public Person(int id, string firstName, string lastName)
  {
     if(String.IsNullOrWhiteSpace(firstName))
        throw new ArgumentNullException("firstName");
     if(String.IsNullOrWhiteSpace(lastName))
        throw new ArgumentNullException("lastName");
     if(id < 0)
        throw new ArgumentOutOfRangeException("id");


     this.id=id;
     this.firstName = firstName ;
     this.lastName = lastName ;
  }
omikad
  • 979
  • 8
  • 10
  • public static bool IsNullOrWhiteSpace is not private but public static method of class String and what you do here is just calling it from ctor not in a method – Fabjan Jul 09 '15 at 10:11
1

If you use a private setter, the property can be set through reflection, so it is not immutable. This may or may not be a problem for you in this example. Personally I prefer your method as it is definitely and immutable and other coders can infer the intent easily.

In any case, you because your data is immutable, your errors are also immutable, so will never change. The pattern that you have followed is correct.

You are correct in checking for errors in the constructor, because you don't want to create an immutable object with errors.

[I would have liked to have written this as a comment, instead of an answer, but I am^H^H was 1 point short on reputation. I'll leave it here now.]

F5 F5 F5
  • 159
  • 2
  • 7
  • 2
    The problem with a private setter I would say was not reflection but with somebody later coming along and adding to your class and using that private setter. You'd actually still have the same problem with a private readonly field - reflection lets you get around most things as this question discusses: http://stackoverflow.com/questions/934930/can-i-change-a-private-readonly-field-in-c-sharp-using-reflection – Chris Jul 09 '15 at 09:59
  • Thanks,so my my particular case the private methods I used for checking are okay? correct? –  Jul 09 '15 at 10:02
  • Yes, if these are simple examples of validation that may change/be more complex in real life. But if they are just one-liners, in real-life, I agree with Omikad and would in-line them. But that is just a style thing and they are correct. – F5 F5 F5 Jul 09 '15 at 10:08
1

C# 6 supports immutable getter-only auto properties, so if you are using Visual Studio 2015 you could further refactor like this:

  public int Id { get; }

  public Person(int id, string firstName, string lastName)
  {
     if (id < 0)
        throw new ArgumentOutOfRangeException("id");

     Id=id;
  }

I've restricted the example to the ID property only. The constructor validates the value and assigns it to the property if valid, and the property is fully immutable plus you have not had to explicitly declare a backing field. This is nicely documented with a refactoring demonstration on the Resharper website.

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
0

If it's necessary to mark field as readonly - there is no other option than to do error checking inside ctor. Readonly fields can only be modified in constructor or initialization block.

Simply do something like this :

public Person(int id) {
    if(id > 1200) throw new Exception(); // or put it in separate method
    this.Id = id;
}

You can even go further and use generic method like :

private void CheckValue<T>(T parameter, string name)
{
       if(T is int) if((parameter as int) < 0) throw new ArgumentOutOfRangeException(name));
       if(T is string) if(String.IsEmptyOrWhiteSpace(parameter as string)) throw new ArgumentNullException(name));
      //   ... if (T is) ... other types here
   }
}

in ctor :

public Person(int id, string name) {
    CheckValue(id, "id");
    CheckValue(name, "name");
    this.Id = id;
}
Fabjan
  • 13,506
  • 4
  • 25
  • 52
  • If this is really the better practice, how come in the link I provided, the answer was to make the variable read-only and use the constructor? –  Jul 09 '15 at 09:32
  • 1
    You're right but if your field is readonly it can be changed only from constructor and there is no other way than to do it the way you did (and it's the correct one) – Fabjan Jul 09 '15 at 09:35
  • So the error checking would have to be done by the private method, correct? If I use read-only/constructor ? –  Jul 09 '15 at 09:39
  • Either in private method that is invoked in constructor or constructor itself. – Fabjan Jul 09 '15 at 09:42
  • 1
    So, what I've provided in my example is alright? correct? –  Jul 09 '15 at 09:46
  • 2
    As a matter of taste and opinion I would have the if-statements in the constructor, but other than that, yes. – Lasse V. Karlsen Jul 09 '15 at 09:49
  • 1
    It depends. If you want your class to be 100% immutable then it is correct, yes. – Fabjan Jul 09 '15 at 09:50