My understanding is any method which
does not modify state of the
constaining [sic] class is a prime candidate
to be made static because it does not
touch the instance.
I don't think this is the right way to think of static vs. non-static. Rather, any method which is not related to the state of a class instance can be static. A name is obviously associated with a particular person, so a Name field on a Person class should almost certainly not be static. Otherwise, you could end up with a scenario like this:
public class Person {
public static string Name { get; set; }
public Person(string name) { Name = name; }
public override string ToString() {
return Name;
}
}
Person dan = new Person("Dan";
Person john = new Person("John";
// outputs "John"
Console.WriteLine(john);
// outputs "John" again, since Dan doesn't have a name property all his own
// (and neither does John, for that matter)
Console.WriteLine(dan);
EDIT: Now, suppose we have a property such as House which can belong to not one but several people. blade asks: "How would this be modeled in code - static?"
My answer: NO, for the same reason already mentioned above. Suppose I've fixed the problem above by making the Name property non-static. Then I introduce a new static House property. Now I have a situation like this:
public class Person {
public string Name { get; set; }
public static House House { get; set; }
public Person(string name, House house) {
Name = name;
House = house;
}
public override string ToString() {
return String.Concat(Name, ", ", House);
}
}
public class House {
public double SquareFootage { get; set; }
public House(double sqft) { SquareFootage = sqft; }
public override string ToString() {
return String.Format("House - {0} sq. ft.", SquareFootage);
}
}
House danAndKatsHouse = new House(1000.0);
House johnsHouse = new House(2000.0);
Person dan = new Person("Dan", danAndKatsHouse);
// outputs "Dan, House - 1000 sq. ft." as expected
Console.WriteLine(dan);
Person kat = new Person("Kat", danAndKatsHouse);
// outputs "Kat, House - 1000 sq. ft.", again as expected
Console.WriteLine(kat);
Person john = new Person("John", johnsHouse);
// outputs "John, House - 2000 sq. ft.", so far so good...
Console.WriteLine(john);
// but what's this? suddenly dan and kat's house has changed?
// outputs "Dan, House - 2000 sq. ft."
Console.WriteLine(dan);
There's a difference between multiple objects of one class sharing the same object and ALL objects of that class sharing the same object. In the latter case, a static property makes sense; otherwise, it does not.
There are a few ways off the top of my head to deal with this scenario. They are:
1. Make the House property non-static
It might seem strange, but you can always just make the House property non-static and you won't ever have to worry about the problem above. Furthermore, assuming you actually do assign the same House object to each Person who shares it, making a change to the House through one Person object will actually achieve the desired effect:
House danAndKatsHouse = new House(1000.0);
// (after making Person.House property non-static)
Person dan = new Person("Dan", danAndKatsHouse);
Person kat = new Person("Kat", danAndKatsHouse);
dan.House.SquareFootage = 1500.0;
// outputs "1500"
Console.WriteLine(kat.House.SquareFootage);
This could actually be a problem, however, if you accidentally assign the same House to two people with the intention of them actually having two different Houses:
House smallerHouse = new House(1000.0);
House biggerHouse = new House(2000.0);
Person dan = new Person("Dan", smallerHouse);
Person john = new Person("John", biggerHouse);
Person bill = new Person("Bill", smallerHouse);
bill.House.SquareFootage = 1250.0;
// yikes, Dan's house just changed...
Console.WriteLine(dan);
2. Use a database
The fact is that the notion of making a house a property of a person is somewhat a forced concept to begin with. A person may move out of their house, different people may move in, etc. Really a relationship exists between the two, which makes using a database an appropriate solution.
Using this approach, what you'd do is have a Persons table, a Houses table, and a third table (maybe PersonHouses) containing id pairs from the other two tables to represent, I guess, home ownership.
If you don't have a full-blown database at your disposal, you can achieve effectively the same result (in .NET) using a System.Data.DataSet and its collection of System.Data.DataTable objects in your code. However, in any scenario where you are translating rows of a database (or DataTable) to objects and then back again, you do need to be aware of impedence mismatch. Basically, whatever meticulous precautions you take in your code to encapsulate your data are out the window once that data is in the database, ready to be modified by anyone with sufficient permissions.
3. Use dictionaries
Another approach, similar to using a database but a bit more work (and I think some programmers who've gone the DataSet route shudder at the thought of this), is to use dictionaries to keep track of your Person and House objects. The quickest way to implement this approach would be to have a Dictionary<int, Person> and a Dictionary<int, House>, and assign each Person a HouseId property:
public class Person {
public int Id { get; private set; }
public string Name { get; set; }
public int HouseId { get; set; }
private static int LastIdValue { get; set; }
private static Dictionary<int, Person> People { get; set; }
static Person() {
LastIdValue = 0;
People = new Dictionary<int, Person>();
}
// make the constructor private to disallow direct instantiation
private Person(int id, string name, int houseId) {
Id = id;
Name = name;
HouseId = houseId;
}
// only permit construction through this function, which inserts
// the new Person into the static dictionary before returning it
static public Person NewPerson(string name, int houseId) {
Person p = new Person(LastIdValue++, name, houseId);
People.Add(p.Id, p);
return p;
}
static public Person getPersonById(int id) {
Person p = null;
return People.TryGetValue(id, out p) ? p : null;
}
}
public class House {
public int Id { get; private set; }
public int SquareFootage { get; set; }
private static int LastIdValue { get; set; }
private static Dictionary<int, House> Houses { get; set; }
static House() {
LastIdValue = 0;
Houses = new Dictionary<int, House>();
}
// make the constructor private to disallow direct instantiation
private House(int id, int sqft) {
Id = id;
SquareFootage = sqft;
}
// only permit construction through this function, which inserts
// the new House into the static dictionary before returning it
static public House NewHouse(int sqft) {
House h = new House(LastIdValue++, sqft);
Houses.Add(h.Id, h);
return h;
}
static public House getHouseById(int id) {
House h = null;
return Houses.TryGetValue(id, out h) ? h : null;
}
}
House firstHouse = House.NewHouse(1000.0);
House secondHouse = House.NewHouse(2000.0);
Person dan = Person.NewPerson("Dan", firstHouse.Id);
Person kat = Person.NewPerson("Kat", firstHouse.Id);
Person john = Person.NewPerson("John", secondHouse.Id);
House dansHouse = House.getHouseById(dan.HouseId);
House katsHouse = House.getHouseById(kat.HouseId);
// this prints
if (katsHouse == dansHouse) { Console.WriteLine("Dan and Kat live in the same house."); }
// this also prints
if (dansHouse == firstHouse) { Console.WriteLine("Dan and Kat live in the first house."); }
// this does not print
if (dansHouse == secondHouse) { Console.WriteLine("Dan and Kat live in the second house."); }
This way, all your data encapsulation still holds. However, if you ever need your data to persist between instances of your code running, then you need to serialize your dictionaries in some file format (most likely XML), which could then be edited by anyone with sufficient privileges, and then you're back to the impedence mismatch problem of using a database all over again.
In light of the advantages and disadvantages of each approach, I think what actually makes the most sense -- and makes your life easiest -- is to simply make House a non-static property. This is assuming you don't have tons and tons of Person and House objects to keep track of.