Which one is the best practice for a constructor? - c#

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 Person(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 Person(string fn, string ln)
{
FirstName = fn;
LastName = ln;
}
}

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.

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 Person(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)

Related

C# - Set two attributes with displaymember in ListBox component

I try to display "FirstName" and "LastName" attributes in my ListBox component on the same row like this "SMITH Robert" but when I launch the program, I have the Id attribute. The problem is probably that the program doesn't find the attributes "LastName" and "FirstName" ...
My Customer class :
public class Customer
{
private int id;
private string lastName;
private string firstName;
public Customer(int id, string lastName, string firstName)
{
this.id = id;
this.lastName = lastName.ToUpper();
this.firstName = firstName;
}
#region Accessors
public int GetId() { return id; }
public string GetLastName() { return lastName; }
public string GetFirstName() { return firstName; }
public void SetId(int id) { this.id = id; }
public void SetLastName(string lastName) { this.lastName = lastName; }
public void SetFirstName(string firstName) { this.firstName = firstName; }
#endregion
#region Properties
public int Id { get { return id; } set { id = value; } }
public string LastName { get { return lastName; } set { lastName = value; } }
public string FirstName { get { return firstName; } set { firstName = value; } }
#endregion
}
My function which I would to use for this manipulation (customers is a List of customers load from an Access database (this part work)):
// Define where is from data.
lbxCustomers.DataSource = customers;
// Value show in listbox.
lbxCustomers.DisplayMember = "LastNameFirstName";
// Value when row is selected in listbox.
lbxCustomers.ValueMember = "Id";
You need the DisplayMember information to be an existing property in the referencing class:
public string LastNameFirstName {
get {
return lastName + ", " + firstName;
}
}

'Final_Project.Member' does not contain a constructor that takes 2 arguments

while (dbReader.Read())
{
aMember = new Member(dbReader["FirstName"].ToString(), dbReader["LastName"].ToString());
this.lstbxNames.Items.Add(aMember);
}
dbReader.Close();
dbConn.Close();
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Final_Project
{
class Member
{
private string firstname;
private string lastname;
private string currentbalance;
public Member()
{
}
public Member(string lname)
{
string LastName = lname;
}
public string FirstName
{
get { return firstname; }
set { firstname = value; }
}
public string LastName
{
get { return lastname; }
set { lastname = value; }
}
public string CurrentBalance
{
get { return currentbalance; }
set { currentbalance = value; }
}
}
}
I am not sure how to fix this error. I have created a class for 'Member' but I am still getting the error that says it does not contain a constructor that takes 2 arguments. I have added the first part of the code that has the error and the class I created that should have made it work.
aMember = new Member(dbReader["FirstName"].ToString(), dbReader["LastName"].ToString());
so lets pretend those values end up looking like this
aMember = new Member("Bill","Gates");
so rather than a comma, maybe you need to join those fields together??
or change your creation so you send it first and surname, and set both fields as you have option for both in your class.
Add a new constructor to let you pass in first name and last name, then set your properties in it. Currently you only have a constructor that accepts one argument that then sets the LastName property.
public Member(string lname)
{
LastName = lname;
}
//New overloaded constructor for lastname and firstname.
public Member(string lname, string fname)
{
LastName = lname;
FirstName = fname;
}
Modifying a code line you have that is attempting to intialize a new member:
aMember = new Member(dbReader["LastName"].ToString(), dbReader["FirstName"].ToString());
If you would rather make it firstName, lastName for initialization, you could flip the constructor arguments around.
Maybe you want your Final_Project.Member constructor as
public Member(string firstName, string lastName)
{
this.firstname = firstName;
this.lastname = lastName;
}
this will work fine.

C# How correctly work get and set?

I am beginner in programming and I want to ask you probably the easiest question.
I did something like this:
class person
{
private string name;
public string surname;
private int year;
}
class student : person
{
}
class Program
{
static void Main(string[] args)
{
List<student> list = new List<student>();
list.Add(new student()
{
surname = "jordan"
// name ... ???
// year .. ?
});
}
}
How can I correctly use get and set if I have private field or how can I assign a value to name or year?
You can set private property in constructor like this:
public class person
{
private string name { get; set; };
public string surname { get; set; };
private int year { get; set; };
public person(string name, int year)
{
this.name = name;
this.year = year;
}
}
public class student : person
{
public student(string name, int year) : base (name, year) { };
}
and use can be:
list.Add(new student("name", 45)
{
surname = "jordan"
});
(Note the use of Upper case for classes and properties, lower case reserved for fields and local variables).
Declare as public properties like so:
class Person
{
public string Surname {get; set;}
}
Usage:
new Person{
Surname = "jordan"
};
Or with private setters, and set in constructor.
class Person
{
public Person(string surname)
{
Surname = surname;
}
public string Surname {get; private set;}
}
Usage:
new Person("jordan");
Or private fields, also set in constructor (same usage).
class Person
{
private string surname;
public Person(string surname)
{
this.surname = surname;
}
public string Surname {get{return surname;}}
}
Even if the fields are private you can provide public properties. You should do that anyway since the fields should not be accessible from outside, all the more if they are just backing fields for properties.
class person
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private string surname;
public string Surname
{
get { return surname; }
set { surname = value; }
}
private int year;
public int Year
{
get { return year; }
private set { year = value; }
}
}
Now the fields are private and you can change the access modifiers of the properties according to your needs. You can even make the setter private as shown in the Year property.
List<Person> list = new List<Person>();
list.Add(new Person()
{
Name = "Michael",
Surname = "jordan",
});
Now you cannot modify the Year from outside since it's private. You could provide an appropriate constructor to initialize it.

Call method/execute piece of code using Property attribute

Just wondering if it's possible to call a method using property attribute. Basically, I want to set state of the entity when any of the public property changes.
Like we have following code
public class Contact : EntityBase
{
[NotifyChange]
public string FirstName { get; set; }
private void ChangeState()
{
EntityState = EntityState.Modified;
}
}
And when we call
var c = new Contact();
c.FirstName = "John";
before (or after) setting value of FirstName, it call EntityState() function.
Any idea?
It would be much simpler if property is rewritten as:
public class Contact : EntityBase
{
private string _firstName;
[NotifyChange]
public string FirstName
{
get
{
return _firstName;
}
set
{
ChangeState();
_firstName = value;
}
}
private void ChangeState()
{
EntityState = EntityState.Modified;
}
}
Try this:
public class Contact : EntityBase
{
private string _firstName;
public string FirstName
{
get {return _firstName}
set
{
_firstName = value;
EntityState(); // maybe here must by ChangeState()
}
}
private void ChangeState()
{
EntityState = EntityState.Modified;
}
}

Fluent interfaces and inheritance in C#

I'll show a problem by example. There is a base class with fluent interface:
class FluentPerson
{
private string _FirstName = String.Empty;
private string _LastName = String.Empty;
public FluentPerson WithFirstName(string firstName)
{
_FirstName = firstName;
return this;
}
public FluentPerson WithLastName(string lastName)
{
_LastName = lastName;
return this;
}
public override string ToString()
{
return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
}
}
and a child class:
class FluentCustomer : FluentPerson
{
private long _Id;
private string _AccountNumber = String.Empty;
public FluentCustomer WithAccountNumber(string accountNumber)
{
_AccountNumber = accountNumber;
return this;
}
public FluentCustomer WithId(long id)
{
_Id = id;
return this;
}
public override string ToString()
{
return base.ToString() + String.Format(" account number: {0} id: {1}", _AccountNumber, _Id);
}
}
The problem is that when you call customer.WithAccountNumber("000").WithFirstName("John").WithLastName("Smith") you can't add .WithId(123) in the end because return type of the WithLastName() method is FluentPerson (not FluentCustomer).
How this problem usually solved?
Try to use some Extension methods.
static class FluentManager
{
public static T WithFirstName<T>(this T person, string firstName) where T : FluentPerson
{
person.FirstName = firstName;
return person;
}
public static T WithId<T>(this T customer, long id) where T : FluentCustomer
{
customer.ID = id;
return customer;
}
}
class FluentPerson
{
public string FirstName { private get; set; }
public string LastName { private get; set; }
public override string ToString()
{
return string.Format("First name: {0} last name: {1}", FirstName, LastName);
}
}
class FluentCustomer : FluentPerson
{
public long ID { private get; set; }
public long AccountNumber { private get; set; }
public override string ToString()
{
return base.ToString() + string.Format(" account number: {0} id: {1}", AccountNumber, ID);
}
}
after you can use like
new FluentCustomer().WithId(22).WithFirstName("dfd").WithId(32);
You can use generics to achieve that.
public class FluentPerson<T>
where T : FluentPerson<T>
{
public T WithFirstName(string firstName)
{
// ...
return (T)this;
}
public T WithLastName(string lastName)
{
// ...
return (T)this;
}
}
public class FluentCustomer : FluentPerson<FluentCustomer>
{
public FluentCustomer WithAccountNumber(string accountNumber)
{
// ...
return this;
}
}
And now:
var customer = new FluentCustomer()
.WithAccountNumber("123")
.WithFirstName("Abc")
.WithLastName("Def")
.ToString();
A solution where you need fluent interface, inheritance and also some generics...
Anyhow as I stated before: this is the only option if you want to use inheritance and access also protected members...
public class GridEx<TC, T> where TC : GridEx<TC, T>
{
public TC Build(T type)
{
return (TC) this;
}
}
public class GridExEx : GridEx<GridExEx, int>
{
}
class Program
{
static void Main(string[] args)
{
new GridExEx().Build(1);
}
}
Logically you need to configure stuff from most specific (customer) to least specific (person) or otherwise it is even hard to read it despite the fluent interface. Following this rule in most cases you won't need get into trouble. If however for any reason you still need to mix it you can use intermediate emphasizing statements like
static class Customers
{
public static Customer AsCustomer(this Person person)
{
return (Customer)person;
}
}
customer.WIthLastName("Bob").AsCustomer().WithId(10);
public class FluentPerson
{
private string _FirstName = String.Empty;
private string _LastName = String.Empty;
public FluentPerson WithFirstName(string firstName)
{
_FirstName = firstName;
return this;
}
public FluentPerson WithLastName(string lastName)
{
_LastName = lastName;
return this;
}
public override string ToString()
{
return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
}
}
public class FluentCustomer
{
private string _AccountNumber = String.Empty;
private string _id = String.Empty;
FluentPerson objPers=new FluentPerson();
public FluentCustomer WithAccountNumber(string accountNumber)
{
_AccountNumber = accountNumber;
return this;
}
public FluentCustomer WithId(string id)
{
_id = id;
return this;
}
public FluentCustomer WithFirstName(string firstName)
{
objPers.WithFirstName(firstName);
return this;
}
public FluentCustomer WithLastName(string lastName)
{
objPers.WithLastName(lastName);
return this;
}
public override string ToString()
{
return objPers.ToString() + String.Format(" account number: {0}", _AccountNumber);
}
}
And invoke it using
var ss = new FluentCustomer().WithAccountNumber("111").WithFirstName("ram").WithLastName("v").WithId("444").ToString();
Is a fluent interface really the best call here, or would an initializer be better?
var p = new Person{
LastName = "Smith",
FirstName = "John"
};
var c = new Customer{
LastName = "Smith",
FirstName = "John",
AccountNumber = "000",
ID = "123"
};
Unlike a fluent interface, this works fine without inherited methods giving back the base class and messing up the chain. When you inherit a property, the caller really shouldn't care whether FirstName was first implemented in Person or Customer or Object.
I find this more readable as well, whether on one line or multiple, and you don't have to go through the trouble of providing fluent self-decorating functions that correspond with each property.
I know this is now an old question, but I wanted to share my thoughts about this with you.
What about separating fluency, which is a kind of mechanism, and your classes, when you can ? This would leave your classes pure.
What about something like this ?
The classes
public class Person
{
public string FirstName { get; set; }
public string LastName {get; set;}
public override string ToString()
{
return $"First name: {FirstName} last name: {LastName}";
}
}
public class Customer : Person
{
public string AccountNumber { get; set; }
public long Id { get; set; }
public override string ToString()
{
return base.ToString() + $" account number: {AccountNumber} id: {Id}");
}
}
A class that adds some fluent mechanism
public class FluentCustomer
{
private Customer Customer { get; }
public FluentCustomer() : this(new Customer())
{
}
private FluentCustomer(Customer customer)
{
Customer = customer;
}
public FluentCustomer WithAccountNumber(string accountNumber)
{
Customer.AccountNumber = accountNumber;
return this;
}
public FluentCustomer WithId(long id)
{
Customer.Id = id;
return this;
}
public FluentCustomer WithFirstName(string firstName)
{
Customer.FirstName = firstName;
return this;
}
public FluentCustomer WithLastName(string lastName)
{
Customer.LastName = lastName;
return this;
}
public static implicit operator Customer(FluentCustomer fc)
{
return fc.Customer;
}
public static implicit operator FluentCustomer(Customer customer)
{
return new FluentCustomer(customer);
}
}
An extension method to switch to fluent mode
public static class CustomerExtensions
{
public static FluentCustomer Fluent(this Customer customer)
{
return customer;
}
}
The same example as in question
Customer customer = new Customer().Fluent()
.WithAccountNumber("000")
.WithFirstName("John")
.WithLastName("Smith")
.WithId(123);

Categories

Resources