// Example demonstrating class hierarchies and inheritance // Extension of Lecture 3, using Reflection for a generic from of serialisation // This version uses properties to simplify access // ----------------------------------------------------------------------------- using System; using System.Text; using System.Reflection; using System.IO; using System.Runtime.CompilerServices; using System.Diagnostics; // The base class: generic information on a person class Person{ // private data fields for Person; uses a mixture of access modifiers for demonstration private string _fName; private string _lName; private string _address; // public properties to access the data public string fName { get { return _fName; } set { _fName = value; } } public string lName { get { return _lName; } set { _lName = value; } } public string address { get { return _address; } set { _address = value; } } public Person(string fName, string lName, string address){ this.fName = fName; this.lName = lName; this.address = address; } public override string ToString() { return String.Format("Name: {0} {1}\tAddress: {2}", this.fName, this.lName, this.address); } public virtual string Serialise() { return "Person.Serialise: To be implemented"; } } // Student is a sub-class of Person, and inherits its data-fields and methods class Student: Person{ // the private data for Student private string _matricNo; private string _degree; // the properties to access the data public string degree { get { return _degree; } set { _degree = value; } } public string matricNo { get { return _matricNo; } set { _matricNo = value; } } // constructor public Student(string fName, string lName, string address, string matricNo, string degree): base(fName, lName, address) { this.matricNo = matricNo; this.degree = degree; } // override ToString as an example of serialisation public override string ToString() { string base_str = base.ToString(); string this_str = String.Format("MatricNo: {0}\tDegree: {1}", this.matricNo, this.degree); return base_str+"\n"+this_str; } // A different way to implement ToString; it is less generic, // because it relies on knowledge of the field in all parent classes public string ToString0() { return String.Format("Name: {0} {1}\tAddress: {2}\nMatricNo: {3}\tDegree: {4}", this.fName, this.lName, this.address, this.matricNo, this.degree); } } // Lecturer is another sub-class of Person, and inherits its data-fields and methods class Lecturer: Person{ // the pivate data private string _officeNo; // the property to access the data public string officeNo { get { return _officeNo; } // set { _officeNo = value; } } set { // for debugging, a setter that prints changes and callers StackTrace stackTrace = new StackTrace(); MethodBase methodBase = stackTrace.GetFrame(1).GetMethod(); Console.WriteLine("setter for officeNo was called by : " + methodBase.Name); _officeNo = value; } } public Lecturer(string fName, string lName, string address, string officeNo): base(fName, lName, address) { this.officeNo = officeNo; } // override ToString as an example of serialisation public override string ToString() { string base_str = base.ToString(); string this_str = String.Format("OfficeNo: {0}", this.officeNo); return base_str+"\n"+this_str; } public override string Serialise() { string str = ""; Type type = this.GetType(); Console.WriteLine("\nDoing {0}.Serialise() ...", type.Name); PropertyInfo[] properties = type.GetProperties(); Console.WriteLine("Found {0} fields ...", properties.Length); foreach(PropertyInfo propertyInfo in properties) { str += String.Format("\t{0}: {1}", propertyInfo.Name, propertyInfo.GetValue(this, null)); } return str; } public string Serialise1() { string str = ""; Type type = this.GetType(); Console.WriteLine("\nDoing {0}.Serialise1() ...", type.Name); MethodInfo methodInfo = type.GetMethod("ToString"); str += methodInfo.Invoke(this, null); /* ConstructorInfo ctor = type.GetConstructor(System.Type.EmptyTypes); if(ctor != null) { object instance = ctor.Invoke(null); MethodInfo methodInfo = type.GetMethod("ToString"); str += methodInfo.Invoke(instance, null); } */ return str; } } class Test{ public static void Main(){ Person p = new Person("John", "Smith", "Edinburgh"); Student s = new Student("Brian", "Hillman", "London", "99124678", "CS"); Lecturer l = new Lecturer("Hans-Wolfgang", "Loidl", "Edinburgh", "G48"); Console.WriteLine("\nHere we use the overriden ToString() method, implemented as a generic serialisation method:"); Console.WriteLine("Student: {0} ", s.ToString0()); Console.WriteLine("Student: {0} ", s.ToString()); Console.WriteLine("Lecturer: {0} ", l.ToString()); Console.WriteLine("\nNow we use Reflection in order to implement a generic serialisation function:"); Console.WriteLine("Lecturer: {0} ", l.Serialise()); l.officeNo = "G59"; // move office; trace it with messages in the setter Console.WriteLine("Lecturer: {0} ", l.Serialise()); Console.WriteLine("Lecturer: {0} ", l.Serialise1()); Console.WriteLine("Person: {0} ", p.Serialise()); } }