// visitor pattern for array of students using System; using System.Collections.Generic; public interface student // visitee { object accept(svisitor sv); } public interface svisitor // visitor { object visit(undergrad x); object visit(grad x); } // notice: no need for visit methods to do any type casting public class stubase // base class of all students { public readonly string name; // readonly = assign once public double gpa; public double tuition; public stubase(string n) {name=n;} }// student base class public class undergrad : stubase, student { public string major; public undergrad(string n, string m) : base(n) { major = m; gpa = (new Random().Next(100,400))/100.0; tuition = 1500; } // important to understand what's resolved statically and dynamically: public object accept(svisitor v) { return v.visit(this); } }//undergrad public class grad : stubase, student { public string thesis; public grad(string n, string t) : base(n) { thesis = t; gpa = (new Random().Next(250,400))/100.0; tuition = 2000; } public object accept(svisitor v) { return v.visit(this); } } // probation/honors evaluation public class evaluator : svisitor { public object visit(undergrad x) { if (x.gpa<2.0) return "probation"; else if (x.gpa>3.5) return "dean's list"; return "normal"; } public object visit(grad x) { if (x.gpa<3.0) return "probation"; else if (x.gpa>3.8) return "honors"; return "normal"; } }//evaluator public class reporter : svisitor { public object visit(undergrad x) { Console.WriteLine("Undergrad "+x.name+" majors in "+x.major+" and has a gpa of "+x.gpa); return null; } public object visit(grad x) { Console.WriteLine("Grad student "+x.name+" is writing a thesis on "+x.thesis+" and has a gpa of "+x.gpa); return null; } } // compute and report average gpas of grads and undergrads for a list public class gpaaverager : svisitor { List SL; // pointer to list of students double ugpa; // undergrad total gpa int ucount; // undergrad count double ggpa; // grad total gpa int gcount; // grad count public gpaaverager(List l) { SL = l; } public void run() { foreach (student s in SL) s.accept(this); Console.WriteLine("Average undergrad gpa is "+(ugpa/ucount)); Console.WriteLine("Average grad gpa is "+(ggpa/gcount)); } public object visit(undergrad x) { ugpa += x.gpa; ucount++; return null; } public object visit(grad x) { ggpa += x.gpa; gcount++; return null; } }//gpaaverager public class students2018 { public static void Main() { List L = new List(); L.Add(new undergrad("Nev Erstudy","cs")); L.Add(new grad("Farma Nimal","P=NP?")); L.Add(new undergrad("Haten Umbers","ee")); L.Add(new grad("F Arman Imal","distributed fault tolerence")); svisitor v1 = new evaluator(); foreach (student s in L) Console.WriteLine(s.accept(v1)); svisitor v2 = new reporter(); foreach (student s in L) s.accept(v2); svisitor v3 = new gpaaverager(L); ((gpaaverager)v3).run(); }//main } /// Now, how about a whole new kind of students, must extend modularly public interface evisitor : svisitor // extended student visitor { object visit(weirdstudent w); } public class ereporter : reporter, evisitor { public object visit(weirdstudent w) { Console.WriteLine("weirdo has a gpa of -1 and pays no tuition"); return null; } } public class weirdstudent : stubase,student { public weirdstudent(): base("weirdo") { gpa = -1; // that's weird! tuition = 0; } public object accept(evisitor v) { return v.visit(this); }//ok, but ... // dilemma: must have following in order to implement student, // it won't compile as is, why??? //public object accept(svisitor v) { return v.visit(this); }//won't compile! public object accept(svisitor v) { if (v is evisitor) return accept((evisitor)v); else { Console.WriteLine("this visitor can't visit weirdo"); return null; } } }