// visitor pattern for array of students using System; using System.Collections.Generic; // for List structure public interface student // visitee { object accept(svisitor sv); } public interface svisitor // visitor { object visitu(undergrad x); object visitg(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.visitu(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.visitg(this); } } // probation/honors evaluation public class evaluator : svisitor { public object visitu(undergrad x) { if (x.gpa<2.0) return "probation"; else if (x.gpa>3.5) return "dean's list"; return "normal"; } public object visitg(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 visitu(undergrad x) { Console.WriteLine("Undergrad "+x.name+" majors in "+x.major+" and has a gpa of "+x.gpa); return null; } public object visitg(grad x) { Console.WriteLine("Grad student "+x.name+" is writing a thesis on "+x.thesis+" and has a gpa of "+x.gpa); return null; } } // superclass for visiting a list of students, can also be abstract class public class listvisitor : svisitor // superclass { protected List SL; // pointer to list of students protected listvisitor(List l) { SL=l; } public virtual object run() // call this from outside class { foreach (student s in SL) s.accept(this); return report(); } // methods that should be overriden in subclass: public virtual object visitu(undergrad x) { return null; } public virtual object visitg(grad x) { return null; } public virtual object report() { return null; } }//listvisitor superclass // compute and report average gpas of grads and undergrads for a list public class gpaaverager : listvisitor { double ugpa; // undergrad total gpa int ucount; // undergrad count double ggpa; // grad total gpa int gcount; // grad count public gpaaverager(List l) : base(l) { ugpa = 0; ggpa = 0; ucount=gcount=0; } public override object visitu(undergrad x) { ugpa += x.gpa; ucount++; return null; } public override object visitg(grad x) { ggpa += x.gpa; gcount++; return null; } public override object report() { Console.WriteLine("Average undergrad gpa is "+(ugpa/ucount)); Console.WriteLine("Average graduate gpa is "+(ggpa/gcount)); return null; } }//gpaaverager subclass public class students2019 { 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("Barna N. 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); ((listvisitor)v3).run(); }//main } /// Now, how about a whole new kind of students, must extend modularly public interface evisitor : svisitor // extended student visitor { object visitw(weirdstudent w); } public class ereporter : reporter, evisitor { public object visitw(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.visitw(this); }// useless. WHY? // overloading doesn't change anything, except become more confusing! //public object accept(svisitor v) { return v.visit(this); }//won't compile! public object accept(svisitor v) { if (v is evisitor) return ((evisitor)v).visitw(this); else { Console.WriteLine("this visitor can't visit a weirdo"); return null; } } }// weird student