2つの規則の違いを知りたいのですが:
- 後で派生クラスに実装される抽象メソッドを使用して抽象基本クラスを作成します。
- 抽象メソッドを使用せずに抽象基本クラスを作成し
ますが、派生クラスのレベルで関連するメソッドを後で追加します。
違いはなんですか?
2つの規則の違いを知りたいのですが:
違いはなんですか?
インターフェイスと同様に、抽象クラスは、タイプの既知の操作のセットを表現するように設計されています。ただし、インターフェイスとは異なり、抽象クラスを使用すると、任意の派生型で使用できる共通/共有機能を実装できます。例えば:
public abstract class LoggerBase
{
public abstract void Write(object item);
protected virtual object FormatObject(object item)
{
return item;
}
}
上記のこの本当に基本的な例では、基本的に2つのことを行いました。
派生タイプにLoggerBase
はWrite
メソッドがあることがわかっているので、それを呼び出すことができます。インターフェースとしての上記と同等のものは次のようになります。
public interface ILogger
{
void Write(object item);
}
抽象クラスとして、FormatObject
オプションでオーバーライドできる追加のサービスを提供できます。たとえば、次のように記述している場合ですConsoleLogger
。
public class ConsoleLogger : LoggerBase
{
public override void Write(object item)
{
Console.WriteLine(FormatObject(item));
}
}
メソッドを仮想としてマークするFormatObject
ことで、共有実装を提供できることを意味します。私もそれをオーバーライドすることができます:
public class ConsoleLogger : LoggerBase
{
public override void Write(object item)
{
Console.WriteLine(FormatObject(item));
}
protected override object FormatObject(object item)
{
return item.ToString().ToUpper();
}
}
したがって、重要な部分は次のとおりです。
abstract
クラスは継承する必要があります。abstract
メソッドは派生型で実装する必要があります。virtual
メソッドは、派生型でオーバーライドできます。2番目のシナリオでは、抽象基本クラスに機能を追加しないため、基本クラスのインスタンスを直接処理するときにそのメソッドを呼び出すことはできません。たとえば、実装した場合ConsoleLogger.WriteSomethingElse
、から呼び出すことはできませんでしたLoggerBase.WriteSomethingElse
。
抽象メソッドを基本クラスに配置し、それらをサブクラスに実装するという考え方は、特定のサブクラスの代わりに親型を使用できるということです。たとえば、配列を並べ替えたいとします。基本クラスを次のように定義できます
abstract class Sorter {
public abstract Array sort(Array arr);
}
次に、サブクラスでクイックソート、マージソート、ヒープソートなどのさまざまなアルゴリズムを実装できます。
class QuickSorter {
public Array sort(Array arr) { ... }
}
class MergeSorter {
public Array sort(Array arr) { ... }
}
アルゴリズムを選択して、並べ替えオブジェクトを作成します。
Sorter sorter = QuickSorter();
sorter
これで、内部でクイックソートであるという事実を明らかにすることなく、通り過ぎることができます。あなたが言う配列をソートするには
Array sortedArray = sorter.sort(someArray);
このようにして、実装の詳細(使用するアルゴリズム)がインターフェースからオブジェクトに分離されます(配列をソートするという事実)。
具体的な利点の1つは、ある時点で別の並べ替えアルゴリズムが必要な場合に、他の場所で変更しなくても、この1行でQuickSort()
言うように変更できることです。MergeSort
親にメソッドを含めない場合は、を呼び出すたびsort()
にダウンキャストする必要があります。そうすると、アルゴリズムの変更がより困難になります。QuickSorter
sort()
ケース1)では、正確なタイプを知らなくても、抽象基本タイプからこれらのメソッドにアクセスできます(抽象メソッドは仮想メソッドです)。
抽象クラスのポイントは通常、派生クラスによって実装される基本クラスに何らかのコントラクトを定義することです(このコンテキストでは、インターフェイスが一種の「純粋な抽象クラス」であることを認識することが重要です)。
ええと、違いは、基本クラスが前者について知っているのであって、後者については知らないということです。
つまり、基本クラスの抽象メソッドを使用すると、その抽象メソッドを呼び出す基本クラスの他のメソッドにコードを記述できます。
明らかに、基本クラスにそれらのメソッドがない場合...それらを呼び出すことはできません...
抽象関数は機能を持つことができません。基本的に、子クラスはこのメソッドの独自のバージョンを提供する必要があると言っていますが、親クラスに実装しようとすることすら一般的すぎます。仮想関数は、基本的に外観を言っています。これが、子クラスにとって十分である場合とそうでない場合がある機能です。したがって、それで十分な場合は、この方法を使用し、そうでない場合は、私をオーバーライドして、独自の機能を提供してください...
もちろん、仮想メソッドをオーバーライドする場合は、次を呼び出すことでいつでも親メソッドを参照できます。base.myVirtualMethod()
さて、あなたがこのような方法を見たら:
A.Foo();
あなたが本当に持っているのは(舞台裏で)このような署名です。
Foo(A x);
そして、あなたが呼び出すとき、A.Foo()
あなたは本当にFoo(this)
どこthis
にタイプAのオブジェクトへの参照があるかを呼んでいます。
ここで、タイプA、B、C、またはDのいずれかをとることができるメソッドがFoo(A|B|C|D...)
どこにあるかを知りたい場合があります。ただし、渡すタイプについて心配する必要はなく、必要なだけです。渡されたタイプに基づいて異なることを行うため。抽象的なメソッドを使用すると、それが可能になります。それが唯一の目的です。Foo