namespace studentstuff { ////////////// using System; public interface student { object accept(svisitor v); } public interface svisitor { object visitg(grad g); object visitu(undergrad x); object visitn(nostudent x); } public class studentbase { internal string name; // internal = private to assembly internal double gpa; internal int tuition; public studentbase(string n, double g) { name=n; gpa=g; } internal student next; // linked list pointer } public class undergrad : studentbase, student { internal string major; public undergrad(string n, double g, string m) : base(n,g) { major = m; tuition = 3; } public object accept(svisitor v) {return v.visitu(this);} } public class grad : studentbase, student { internal string thesis; public grad(string n, double g, string m) : base(n,g) { thesis = m; tuition = 5; } public object accept(svisitor v) {return v.visitg(this);} } public class nostudent : student { public object accept(svisitor v) {return v.visitn(this);} } //////// visitors public class printingvisitor : svisitor { public object visitu(undergrad u) { Console.WriteLine("undergrad "+u.name+" has gpa "+u.gpa+ " and majors in "+u.major); return u.next.accept(this); } public object visitg(grad g) { Console.WriteLine("grad student "+g.name+" has gpa "+g.gpa+ " and thesis "+g.thesis); return g.next.accept(this); } public object visitn(nostudent n) { return null; } } //printer public class gpaaverager : svisitor { protected double sum = 0; protected int count = 0; public object visitu(undergrad s) { sum += s.gpa; count++; return s.next.accept(this); } public object visitg(grad s) { sum += s.gpa; count++; return s.next.accept(this); } public object visitn(nostudent s) { return sum/count; //double } } //test public class students1 { public static void Main() { undergrad a = new undergrad("mary",3.5,"cs"); grad b = new grad("harry",3.0,"oop design patterns"); undergrad c = new undergrad("larry",2.0,"polisci"); a.next = b; b.next = c; c.next = new nostudent(); a.accept(new printingvisitor()); gpaaverager x = new gpaaverager(); double ave = (double)a.accept(x); Console.WriteLine("average gpa is "+ave); // the following tests the weirdstudent extension (see after main) weirdstudent d = new weirdstudent("bob",0,2000); weirdstudent e = new weirdstudent("jane",4.0,20000); c.next = new sadaptor(d); // d needs to be adapted to student interface d.next = new sadaptor(e); e.next = new nostudent(); string s = (string)a.accept(new mostweird()); Console.WriteLine(s+" is the most weird"); double ave2 = (double)a.accept(new eaverager()); Console.WriteLine("average gpa is "+ave2); } //main } ///// Now I want to add a new kind of student: (weirdstudent) public interface estudent { object accept(evisitor ev); } // Why doesn't this interface extend the student interface? Because then // it must implement accept(svisitor v). But an svisitor doesn't know // how to visit an estudent, only a "regular" student. public interface evisitor : svisitor { object visitw(weirdstudent w); } // An object adaptor class allows a estudent object to be treated as // a regular student object, for the purpose of uniformity: without // wrapping an estudent inside an adaptor, we cannot insert a weirdstudent // into a list of regular students. This is needed because the estudent // interface does not simply extend the student interface. public class sadaptor : student { private estudent es; public sadaptor(estudent x) {es = x;} public object accept(svisitor v) { return es.accept((evisitor)v); } // may throw casting exception } public class weirdstudent : studentbase, estudent { internal int weirdness; // weirdness rank public weirdstudent(string n, double g, int w) : base(n,g) { weirdness=w; } public object accept(evisitor ev) {return ev.visitw(this);} // public object accept(svisitor v) // { throw new Exception("I'm too weird for v"); } } //// sample evisitor finds student that's weirdest public class mostweird : evisitor { private string weirdo = ""; // name of weirdest student private int w = 0; // keep track of highest weirdness rank public object visitg(grad g) { return g.next.accept(this); } public object visitu(undergrad u) { return u.next.accept(this); } public object visitw(weirdstudent ws) { if (w