サーチ…
構文
- クラスDerivedClass:BaseClass
- クラスDerivedClass:BaseClass、IExampleInterface
- クラスDerivedClass:BaseClass、IExampleInterface、IAnotherInterface
備考
クラスは1つのクラスから直接継承することができますが、(代わりに、または同時に)1つ以上のインタフェースを実装できます。
構造体はインタフェースを実装できますが、どの型からも明示的に継承することはできません。それらはSystem.ValueType
から暗黙的に継承され、 System.Object
から直接継承します。
基底クラスから継承
コードの重複を避けるには、一般的なクラスの一般的なメソッドと属性をベースとして定義します。
public class Animal
{
public string Name { get; set; }
// Methods and attributes common to all animals
public void Eat(Object dinner)
{
// ...
}
public void Stare()
{
// ...
}
public void Roll()
{
// ...
}
}
今度はAnimal
を表すクラスがありますので、特定の動物の特異性を表すクラスを定義できます:
public class Cat : Animal
{
public Cat()
{
Name = "Cat";
}
// Methods for scratching furniture and ignoring owner
public void Scratch(Object furniture)
{
// ...
}
}
Catクラスは、その定義に明示的に記述されているメソッドだけでなく、一般的なAnimal
基本クラスで定義されているすべてのメソッドにもアクセスできます。どの動物(猫であろうとなかろうと)は食べることができます。しかし、それが猫でもない限り、動物はスクラッチすることができませんでした。その後、他の動物を記述する他のクラスを定義することができます。 (Gopherのような、花の庭とSlothを破壊する方法は、余分な方法を一切持たない)。
クラスから継承し、インタフェースを実装する
public class Animal
{
public string Name { get; set; }
}
public interface INoiseMaker
{
string MakeNoise();
}
//Note that in C#, the base class name must come before the interface names
public class Cat : Animal, INoiseMaker
{
public Cat()
{
Name = "Cat";
}
public string MakeNoise()
{
return "Nyan";
}
}
クラスから継承し、複数のインタフェースを実装する
public class LivingBeing
{
string Name { get; set; }
}
public interface IAnimal
{
bool HasHair { get; set; }
}
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";
}
}
継承のテストと移動
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
抽象基本クラスの拡張
実装のための契約として記述できるインタフェースとは異なり、抽象クラスは拡張のための契約の役目を果たします。
抽象クラスをインスタンス化することはできません。拡張する必要があり、結果のクラス(または派生クラス)をインスタンス化できます。
抽象クラスは汎用実装を提供するために使用されます
public abstract class Car
{
public void HonkHorn() {
// Implementation of horn being honked
}
}
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.
}
上記の例は、Carを継承するすべてのクラスが、実装で自動的にHonkHornメソッドを受け取る方法を示しています。これは、新しいカーを作成する開発者は、どのようにそれがホーンをどのように鳴らすか心配する必要はないことを意味します。
サブクラス内のコンストラクタ
あなたは、基本クラスのサブクラスを作成するときは、使用して基本クラスを構築することができます: base
サブクラスのコンストラクタのパラメータの後に。
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;
}
}
継承。コンストラクタの呼び出しシーケンス
子クラスのDog
を持つAnimal
クラスがあると考えてください
class Animal
{
public Animal()
{
Console.WriteLine("In Animal's constructor");
}
}
class Dog : Animal
{
public Dog()
{
Console.WriteLine("In Dog's constructor");
}
}
デフォルトでは、すべてのクラスは暗黙的にObject
クラスを継承します。
これは上記のコードと同じです。
class Animal : Object
{
public Animal()
{
Console.WriteLine("In Animal's constructor");
}
}
Dog
クラスのインスタンスを作成するとき、親クラスの別のコンストラクタへの明示的な呼び出しがない場合 、 基本クラスのデフォルトのコンストラクタ(パラメータなし)が呼び出されます。私たちの場合、最初はObject's
コンストラクタ、次にAnimal's
、そして最後のDog's
コンストラクタと呼ばれます。
public class Program
{
public static void Main()
{
Dog dog = new Dog();
}
}
出力は
動物のコンストラクタで
犬のコンストラクタで
親のコンストラクタを明示的に呼び出します。
上の例では、 Dog
クラスのコンストラクタがAnimal
クラスのデフォルトのコンストラクタを呼び出します 。必要に応じて、呼び出すコンストラクタを指定できます。つまり、親クラスで定義されているコンストラクタを呼び出すことができます。
これら2つのクラスがあると考えてください。
class Animal
{
protected string name;
public Animal()
{
Console.WriteLine("Animal's default constructor");
}
public Animal(string name)
{
this.name = name;
Console.WriteLine("Animal's constructor with 1 parameter");
Console.WriteLine(this.name);
}
}
class Dog : Animal
{
public Dog() : base()
{
Console.WriteLine("Dog's default constructor");
}
public Dog(string name) : base(name)
{
Console.WriteLine("Dog's constructor with 1 parameter");
Console.WriteLine(this.name);
}
}
ここには何がありますか?
各クラスには2つのコンストラクタがあります。
base
どういう意味ですか?
base
は親クラスへの参照です。私たちの場合、このようなDog
クラスのインスタンスを作成するとき
Dog dog = new Dog();
ランタイムはまず、パラメータのないコンストラクタであるDog()
呼び出します。しかし、その体はすぐには機能しません。コンストラクタのかっこの後に、 base()
というような呼び出しがあります。つまり、デフォルトのDog
コンストラクタを呼び出すと、親のデフォルトコンストラクタが呼び出されます。親のコンストラクタが実行されると、 Dog()
コンストラクタ本体が返され、最後に実行されます。
出力は次のようになります:
動物のデフォルトコンストラクタ
犬のデフォルトコンストラクタ
Dog's
コンストラクタをパラメータで呼び出すとどうなりますか?
Dog dog = new Dog("Rex");
プライベートではない親クラスのメンバーは、子クラスによって継承されます。つまり、 Dog
はname
フィールドもあります。
このケースでは、コンストラクタに引数を渡しました。彼は、引数を親クラスのコンストラクタに渡して、 name
フィールドを初期化します。
出力は
Animal's constructor with 1 parameter
Rex
Dog's constructor with 1 parameter
Rex
概要:
すべてのオブジェクトの作成は基本クラスから開始されます。継承では、階層にあるクラスが連鎖されます。すべてのクラスはObject
から派生しているため、 Object
作成時に呼び出される最初のコンストラクタはObject
クラスのコンストラクタです。次に、チェーン内の次のコンストラクタが呼び出され、それらのすべてが呼び出された後でのみ、オブジェクトが作成されます
ベースキーワード
- baseキーワードは、派生クラス内から基本クラスのメンバーにアクセスするために使用されます。
- 別のメソッドでオーバーライドされた基本クラスのメソッドを呼び出します。派生クラスのインスタンスを作成するときに呼び出すベースクラスコンストラクタを指定します。
継承するメソッド
メソッドの継承にはいくつかの方法があります
public abstract class Car
{
public void HonkHorn() {
// Implementation of horn being honked
}
// 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
}
}
継承アンチパターン
不適切な継承
2つのクラスのクラスFoo
とBar
ます。 Foo
はDo1
とDo2
2つの機能があります。 Bar
はFoo
Do1
を使用する必要がありますが、 Do2
必要とせず、 Do2
と同等の機能が必要ですが、まったく異なる機能を必要とします。
悪い方法 : Foo
バーチャルでDo2()
を作成し、 Bar
それをオーバーライドするか、 Do2()
Bar
にthrow Exception
をthrow Exception
public class Bar : Foo
{
public override void Do2()
{
//Does something completely different that you would expect Foo to do
//or simply throws new Exception
}
}
良い方法
Foo
からDo1()
を取り出し、新しいクラスBaz
入れ、 Baz
からFoo
とBar
両方を継承し、 Do2()
別々に実装します
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
}
}
なぜ最初の例が悪いのですか?2番目は良いです:開発者nr2がFoo
で変更を行う必要がある場合、 Bar
はFoo
から切り離せないので、 Bar
実装を破る可能性があります。後者の例で行うと、 Foo
とBar
共通点はBaz
移動され、互いに影響しません(そうすべきではありません)。
再帰型指定を持つ基本クラス
再帰型指定子を持つ汎用基本クラスの一回の定義。各ノードには、1つの親ノードと複数の子ノードがあります。
/// <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; } }
}
オブジェクトのツリー階層を定義する必要があるたびに上記を再利用することができます。ツリー内のノードオブジェクトは、ベースクラスから継承する必要があります。
public class MyNode : Tree<MyNode>
{
// stuff
}
各ノードクラスは階層内のどこに親オブジェクトがあるのか、子オブジェクトが何であるかを知っています。いくつかの組み込み型は、 Control
やXmlElement
などのツリー構造を使用し、上記のTree<T>
はコード内の任意の型の基本クラスとして使用できます。
たとえば、 すべての子のウェイトから合計ウェイトを計算するパーツの階層を作成するには、次のようにします。
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); } }
}
として使用する
// [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;
もう1つの例は、相対座標フレームの定義にあります。この場合、座標フレームの実際の位置は、 すべての親座標フレームの位置に依存します。
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;
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));
}
}
として使用する
// 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;