Can I have different return types for getter and setter of "valuetype?" properties?

Viewed 392

For example, I have an int? property named Foo, I want the getter to return int?, and the setter to return int(to forbidden null assignment from outside at compile time), which will be like:

private int? foo;
public int? Foo {
    get {
        return foo;
    }
}
public int Foo {
    set {
        foo = value;
    }
}

The code above will not compile, but I think using accessors this way makes sense. I wonder if C# support such a use case. Otherwise I will have to check if the assigned value is not null in the setter at runtime or work around by adding a pair of method GetFoo and SetFoo.

2 Answers

To only have a property, you can simply write:

public int? Foo
{
  get => _Foo;
  set
  {
    if ( !_Foo.Equals(value) )
      _Foo = value.HasValue ? value : throw new NoNullAllowedException();
  }
}
private int? _Foo;

Or use a dedicated setter to ensure the not null at compile time as suggested by @SomeBody:

public int? Foo => _Foo;
private int? _Foo;

public void SetFoo(int value) => _Foo = value;

The majority will prefer this last option.

Also to be homogeneous:

private int? _Foo;

public int? GetFoo() => _Foo;
public void SetFoo(int value) => _Foo = value;

But if you need to create en entity, to use serialization or some streaming like JSON helpers as well as Visual Studio Designer or runtime Property Editor, or anything requiring a full real read and write property, like ORMs, the first is to be used.

Thus an instance of the class can remove the data of the field from inside, but others artifacts can't set the property to null, just see that it is null.

If child classes can do that, set the field protected.

If other classes in the assembly as well as childs in this assembly can do that, set the field internal.

If other classes in the assembly as well as childs in any assembly can do that, set the field protected internal.

Remark: initial value of _Foo is null.

No. Different types for the getter and the setter are not possible.

What would you like to do if the setter gets a null?

You could either raise an exception:

public int? Foo
{
    get => _Foo;
    set => _Foo = value ?? throw new ArgumentNullException();
}
private int? _Foo;

(notice that you can only use throw expressions like this in C# 7 and later)

or you could set it to a default value:

public int? Foo
{
    get => _Foo;
    set => _Foo = value ?? 0;
}
private int? _Foo;

Note that inside your class logic, you could still set _Foo to null. I assume that is desired. Otherwise, there would probably be no point in using type int? for the Foo property at all and you could just use type int for it.

Edit

Based on a remark by Jeppe Stig Nilsen, I would like to point out that you could also consider to make your Foo property of type int and introduce a separate boolean field or property to indicate if the Foo value is activated/meaningful. It would be a different data model, but for some scenarios it might be a better fit.

public int Foo { get; set; }
public bool FooIsSet { get; set; }

// Setter method for convenience
public void SetFoo(int? value)
{
    if (value.HasValue)
    {
        this.Foo = value.Value;
        this.FooIsSet = true;
    }
    else
    {
        this.Foo = 0; // optional; you might avoid this line to be able to remember the latest set value
        this.FooIsSet = false;
    }
}
Related