using System; // undegrad, grad, nostudent will implement student. public interface student { object accept(svisitor v); } public interface svisitor { object visit(undergrad x); object visit(grad x); object visit(nostudent x); // default student is nostudent } public class stubase // base class for many student classes: { public readonly string name; private double gpa; public double GPA // example of a "property" { get { return gpa; } set { double v=value; if (v>=0 && v<=4.0) gpa=v; } // value is keyword } public student next; // pointer to next student in list public stubase(string n, double g, student nx) // convenience { name=n; GPA=g; next=nx; } // ok to put the following in the base class? // public object accept(svisitor v) { return v.visit(this); } // NO because the type of *this* will be fixed as stubase. }// stubase public class undergrad: stubase, student { public string major; public undergrad(string n, double g, student nx): base(n,g,nx) { string[] Majors = {"cs","cse","math","physics","chem","art","history"}; major = Majors[ (new Random()).Next(0,Majors.Length) ]; } public object accept(svisitor v) { return v.visit(this); } } public class grad: stubase,student { public string topic; // thesis topic public grad(string n, string t, student nx):base(n,0,nx) { topic =t; GPA = (new Random()).NextDouble() * 4.0; } public object accept(svisitor v) { return v.visit(this); } }//grad public class nostudent : student // just a tag { public object accept(svisitor v) { return v.visit(this); } } ////////////// visitors // gpa averager : computes undergrad/grad gpa separately and combined. // returns overall average public class gpaaverager : svisitor { double ugpa=0, ggpa=0; int ucount=0, gcount=0; public object visit(undergrad x) { ugpa += x.GPA; ucount++; return x.next.accept(this); } public object visit(grad x) { ggpa += x.GPA; gcount++; return x.next.accept(this); } public object visit(nostudent x) { double uave = ugpa/ucount; double gave = ggpa/gcount; Console.WriteLine("Undergrad average gpa = "+uave); Console.WriteLine("Grad average gpa = "+gave); double uratio = (ucount*1.0)/(gcount+ucount); double ave = uave*uratio + gave*(1-uratio); // overall ave ucount = 0; gcount = 0; ugpa = 0; ggpa = 0; // reset return ave; // doubles are also objects (no need to use Double) } }// gpaaverager // probation auditor: finds students on academic probation! // uses loop instead of recursion public class auditor : svisitor { protected bool stop = false; protected student current; public auditor(student start) { current = start; } public void run() // now the visitor is stateful { stop = false; // reset if needed while (!stop) current.accept(this); } public object visit(undergrad x) { if (x.GPA<2.0) Console.WriteLine(x.name+" is on academic probation"); current = x.next; return null; } public object visit(grad x) { if (x.GPA<3.0) Console.WriteLine(x.name+" is on academic probation"); current = x.next; return null; } public object visit(nostudent x) { stop = true; return null; } } /* Compile into stu13.dll with csc /t:library stu13.cs */