Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

public interface INoiseMaker

{

string MakeNoise();

}

//Note that in C#, the base class name must come before the interface names public class Cat : LivingBeing, IAnimal, INoiseMaker

{

public Cat()

{

Name = "Cat"; HasHair = true;

}

public bool HasHair { get; set; }

public string Name { get; set; }

public string MakeNoise()

{

return "Nyan";

}

}

Section 58.5: Constructors In A Subclass

When you make a subclass of a base class, you can construct the base class by using : base after the subclass constructor's parameters.

class Instrument

{

string type; bool clean;

public Instrument (string type, bool clean)

{

this.type = type; this.clean = clean;

}

}

class Trumpet : Instrument

{

bool oiled;

public Trumpet(string type, bool clean, bool oiled) : base(type, clean)

{

this.oiled = oiled;

}

}

Section 58.6: Inheritance Anti-patterns

Improper Inheritance

Lets say there are 2 classes class Foo and Bar. Foo has two features Do1 and Do2. Bar needs to use Do1 from Foo, but it doesn't need Do2 or needs feature that is equivalent to Do2 but does something completely di erent.

Bad way: make Do2() on Foo virtual then override it in Bar or just throw Exception in Bar for Do2()

GoalKicker.com – C# Notes for Professionals

299

public class Bar : Foo

{

public override void Do2()

{

//Does something completely different that you would expect Foo to do //or simply throws new Exception

}

}

Good way

Take out Do1() from Foo and put it into new class Baz then inherit both Foo and Bar from Baz and implement Do2() separately

public class Baz

{

public void Do1()

{

// magic

}

}

public class Foo : Baz

{

public void Do2()

{

// foo way

}

}

public class Bar : Baz

{

public void Do2()

{

// bar way or not have Do2 at all

}

}

Now why first example is bad and second is good: When developer nr2 has to do a change in Foo, chances are he will break implementation of Bar because Bar is now inseparable from Foo. When doing it by latter example Foo and Bar commonalty has been moved to Baz and they do not a ect each other (like the shouldn't).

Section 58.7: Extending an abstract base class

Unlike interfaces, which can be described as contracts for implementation, abstract classes act as contracts for extension.

An abstract class cannot be instantiated, it must be extended and the resulting class (or derived class) can then be instantiated.

Abstract classes are used to provide generic implementations

public abstract class Car

{

public void HonkHorn() {

// Implementation of horn being honked

}

}

GoalKicker.com – C# Notes for Professionals

300

public class Mustang : Car

{

//Simply by extending the abstract class Car, the Mustang can HonkHorn()

//If Car were an interface, the HonkHorn method would need to be included

//in every class that implemented it.

}

The above example shows how any class extending Car will automatically receive the HonkHorn method with the implementation. This means that any developer creating a new Car will not need to worry about how it will honk it's horn.

Section 58.8: Testing and navigating inheritance

interface BaseInterface {}

class BaseClass : BaseInterface {}

interface DerivedInterface {}

class DerivedClass : BaseClass, DerivedInterface {}

var baseInterfaceType = typeof(BaseInterface);

var derivedInterfaceType = typeof(DerivedInterface); var baseType = typeof(BaseClass);

var derivedType = typeof(DerivedClass);

var baseInstance = new BaseClass();

var derivedInstance = new DerivedClass();

Console.WriteLine(derivedInstance is DerivedClass); //True

Console.WriteLine(derivedInstance is DerivedInterface); //True

Console.WriteLine(derivedInstance is BaseClass); //True

Console.WriteLine(derivedInstance is BaseInterface); //True

Console.WriteLine(derivedInstance is object); //True

Console.WriteLine(derivedType.BaseType.Name); //BaseClass

Console.WriteLine(baseType.BaseType.Name); //Object

Console.WriteLine(typeof(object).BaseType); //null

Console.WriteLine(baseType.IsInstanceOfType(derivedInstance)); //True

Console.WriteLine(derivedType.IsInstanceOfType(baseInstance)); //False

Console.WriteLine( string.Join(",",

derivedType.GetInterfaces().Select(t => t.Name).ToArray()));

//BaseInterface,DerivedInterface

Console.WriteLine(baseInterfaceType.IsAssignableFrom(derivedType)); //True Console.WriteLine(derivedInterfaceType.IsAssignableFrom(derivedType)); //True Console.WriteLine(derivedInterfaceType.IsAssignableFrom(baseType)); //False

Section 58.9: Inheriting methods

There are several ways methods can be inherited

public abstract class Car

{

public void HonkHorn() {

// Implementation of horn being honked

}

GoalKicker.com – C# Notes for Professionals

301

// virtual methods CAN be overridden in derived classes public virtual void ChangeGear() {

// Implementation of gears being changed

}

// abstract methods MUST be overridden in derived classes public abstract void Accelerate();

}

public class Mustang : Car

{

//Before any code is added to the Mustang class, it already contains

//implementations of HonkHorn and ChangeGear.

//In order to compile, it must be given an implementation of Accelerate,

//this is done using the override keyword

public override void Accelerate() {

// Implementation of Mustang accelerating

}

//If the Mustang changes gears differently to the implementation in Car

//this can be overridden using the same override keyword as above public override void ChangeGear() {

//Implementation of Mustang changing gears

}

}

Section 58.10: Base class with recursive type specification

One time definition of a generic base class with recursive type specifier. Each node has one parent and multiple children.

///<summary>

///Generic base class for a tree structure

///</summary>

///<typeparam name="T">The node type of the tree</typeparam> public abstract class Tree<T> where T : Tree<T>

{

///<summary>

///Constructor sets the parent node and adds this node to the parent's child nodes

///</summary>

///<param name="parent">The parent node or null if a root</param>

protected Tree(T parent)

{

this.Parent=parent; this.Children=new List<T>(); if(parent!=null)

{

parent.Children.Add(this as T);

}

}

public T Parent { get; private set; }

public List<T> Children { get; private set; } public bool IsRoot { get { return Parent==null; } }

public bool IsLeaf { get { return Children.Count==0; } }

///<summary>

///Returns the number of hops to the root object

///</summary>

public int Level { get { return IsRoot ? 0 : Parent.Level+1; } }

}

GoalKicker.com – C# Notes for Professionals

302

The above can be re-used every time a tree hierarchy of objects needs to be defined. The node object in the tree has to inherit from the base class with

public class MyNode : Tree<MyNode>

{

// stuff

}

each node class knows where it is in the hierarchy, what the parent object is as well as what the children objects are. Several built in types use a tree structure, like Control or XmlElement and the above Tree<T> can be used as a base class of any type in your code.

For example, to create a hierarchy of parts where the total weight is calculated from the weight of all the children, do the following:

public class Part : Tree<Part>

{

public static readonly Part Empty = new Part(null) { Weight=0 }; public Part(Part parent) : base(parent) { }

public Part Add(float weight)

{

return new Part(this) { Weight=weight };

}

public float Weight { get; set; }

public float TotalWeight { get { return Weight+Children.Sum((part) => part.TotalWeight); } }

}

to be used as

//[Q:2.5] -- [P:4.2] -- [R:0.4]

//\

//- [Z:0.8]

var Q = Part.Empty.Add(2.5f); var P = Q.Add(4.2f);

var R = P.Add(0.4f); var Z = Q.Add(0.9f);

// 2.5+(4.2+0.4)+0.9 = 8.0 float weight = Q.TotalWeight;

Another example would in the definition of relative coordinate frames. In this case the true position of the coordinate frame depends on the positions of all the parent coordinate frames.

public class RelativeCoordinate : Tree<RelativeCoordinate>

{

public static readonly RelativeCoordinate Start = new RelativeCoordinate(null, PointF.Empty) {

};

public RelativeCoordinate(RelativeCoordinate parent, PointF local_position) : base(parent)

{

this.LocalPosition=local_position;

}

public PointF LocalPosition { get; set; } public PointF GlobalPosition

{

get

{

if(IsRoot) return LocalPosition;

GoalKicker.com – C# Notes for Professionals

303

var parent_pos = Parent.GlobalPosition;

return new PointF(parent_pos.X+LocalPosition.X, parent_pos.Y+LocalPosition.Y);

}

}

public float TotalDistance

{

get

{

float dist = (float)Math.Sqrt(LocalPosition.X*LocalPosition.X+LocalPosition.Y*LocalPosition.Y);

return IsRoot ? dist : Parent.TotalDistance+dist;

}

}

public RelativeCoordinate Add(PointF local_position)

{

return new RelativeCoordinate(this, local_position);

}

public RelativeCoordinate Add(float x, float y)

{

return Add(new PointF(x, y));

}

}

to be used as

//Define the following coordinate system hierarchy

//o--> [A1] --+--> [B1] -----> [C1]

//|

//+--> [B2] --+--> [C2]

//

|

//

+--> [C3]

var A1 = RelativeCoordinate.Start; var B1 = A1.Add(100, 20);

var B2 = A1.Add(160, 10);

var C1 = B1.Add(120, -40); var C2 = B2.Add(80, -20); var C3 = B2.Add(60, -30);

double dist1 = C1.TotalDistance;

GoalKicker.com – C# Notes for Professionals

304