Home > Net >  Which one is the best practice for a constructor?
Which one is the best practice for a constructor?

Time:10-31

I looked at Microsoft's documentation, the best practice should be the second one. But I'm still puzzled by that. I used both constructors in my program without any problems. I would like to know what exactly the difference is?

public class Person
{
    // fields
    private string _firstName;
    private string _lastName;
    // data accessor
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }
    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }
    // constructor
    public LaySegment(string fn, string ln)
    {
        _firstName = fn;
        _lastName = ln;
    }
}
public class Person
{
    // fields
    private string _firstName;
    private string _lastName;
    // data accessor
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }
    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }
    // constructor
    public LaySegment(string fn, string ln)
    {
        FirstName = fn;
        LastName = ln;
    }
}

CodePudding user response:

I suspect Microsoft is promoting the technique demonstrated in the 2nd example to encourage the use of the setters in the defined properties in the event they are doing anything beyond assigning the value to a member field (as mentioned by Ken White above). At the moment they are not so your puzzlement is understandable.

However, as the code grows, the properties may evolve and take on the following shape (let's assume Person is now implementing the INotifyPropertyChanged interface and utilising a service transmit changes to a peer-to-peer network service like a game):

private readonly ISomePeerToPeerService _peerService;

public LaySegment(string fn, string ln, ISomePeerToPeerServicepeerService peerService)
{
    _firstName = fn;
    _lastName = ln;
    _peerService = peerService;
}
    
public string LastName
{
    get { return _lastName; }
    set 
    { 
        if (Equals(_lastName, value)
            return;

        _lastName = value; 
        OnPropertyChanged()
    }

    private void OnPropertyChanged([CallerMemberName] string memberName = null)
    {
        _peerService.Update(this); // Update peers
        
        // notify stuff
    }
}

...it becomes more important to ensure that all code (including the internals of Person to invoke these properties rather than modifying the fields directly. If not, the custom logic won't be invoked.

Another example is if the person gets married:

public BankAccount SharedBankAccount { get { ... }  set { ... } }

public void MarryTheGroom(Person groom)
{
    LastName = groom.LastName;
    SharedBankAccount = groom.PersonalAccount; // he he
}

Conclusion

It's quite similar to accidentally exposing fields publically and then client code modifies them directly rather than calling some method like TransferFundsBeteenBankAccounts(x, y)

CodePudding user response:

Since the class is mutable, and both first and last name are nullable, it is actually not mandatory for the fields to be set on instantiation. So you could save some typing and just do this:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

and instantiate it this way:

var instance = new Person { FirstName = "John", LastName = "Doe" };

Personally I tend to prefer immutable classes, so I would prefer something more like this:

public class Person
{
    private readonly string _firstName;
    private readonly string _lastName;

    public string FirstName => _firstName;

    public string LastName => _lastName;

    public Person (string firstName, string lastName)
    {
        if (firstName == null) throw new ArgumentNullException("FirstName");
        if (lastName == null) throw new ArgumentNullException("LastName");

        _firstName = firstName;
        _lastName = lastName;
    }
}

With the immutable version you can be certain that, from very the moment of instantiation, the first and last name are valid. So the instance is always valid. With your class you can't, regardless of how you set things in the constructor.

  • Related