非抽象クラス (C#) で抽象メソッドを制限する設計の背後にある理由を知りたいです。
クラスインスタンスには定義がないため、呼び出し可能ではないことを理解していますが、静的メソッドが定義されている場合、インスタンスからも除外されます。抽象メソッドがそのように処理されないのはなぜですか?同じ理由はありますか?
それらは具象クラスで許可され、派生クラスはメソッドの実装を強制できます。基本的には、抽象クラスの抽象メソッドの場合に行われます。
非抽象クラス (C#) で抽象メソッドを制限する設計の背後にある理由を知りたいです。
クラスインスタンスには定義がないため、呼び出し可能ではないことを理解していますが、静的メソッドが定義されている場合、インスタンスからも除外されます。抽象メソッドがそのように処理されないのはなぜですか?同じ理由はありますか?
それらは具象クラスで許可され、派生クラスはメソッドの実装を強制できます。基本的には、抽象クラスの抽象メソッドの場合に行われます。
まず、あなたが求めていることは論理的に意味がないと思います。メソッドがある場合abstract
、それは基本的にメソッドが未完成であることを意味します(@ChrisSinclairが指摘したように)。しかし、それはクラス全体が未完成であることも意味するため、abstract
.
別の言い方をすれば、abstract
ではないクラスにメソッドがabstract
ある場合、それは呼び出すことができないメソッドがあることを意味します。しかし、それはメソッドが役に立たないことを意味します。それを削除しても、すべて同じように機能します。
ここで、例を使用してより具体的にしようとします。次のコードを想像してください。
Animal[] zoo = new Animal[] { new Monkey(), new Fish(), new Animal() };
foreach (Animal animal in zoo)
animal.MakeSound();
ここで、Animal
は非abstract
基本クラス (これが配列に直接入れることができる理由です) でMonkey
あり、Fish
から派生しAnimal
、MakeSound()
はabstract
メソッドです。このコードは何をすべきでしょうか? あなたはそれを明確に述べていませんでしたが、いくつかのオプションを想像できます:
MakeSound()
として型指定された変数を呼び出すことはできませんAnimal
。派生クラスの 1 つとして型指定された変数を使用してのみ呼び出すことができるため、これはコンパイル エラーです。
abstract
派生クラスのインスタンスを基本クラスとして扱い、派生クラスに固有の動作を取得できるようにすることが全体のポイントであるため、これは良い解決策ではありません。これが必要な場合は、各派生クラスに通常の ( no abstract
、virtual
またはoverride
) メソッドを配置するだけで、基本クラスでは何もしません。
MakeSound()
実行時の型が実際には であるオブジェクトを呼び出すことはできないAnimal
ため、これは実行時エラー (例外) です。
これも良い解決策ではありません。C# は静的に型付けされた言語であるため、コンパイル時に「このメソッドを呼び出すことはできません」などのエラーをキャッチしようとします (リフレクションや などの明らかな例外をdynamic
含む)。言語。virtual
また、基本クラスに例外をスローするメソッドを作成することで、これを簡単に行うことができます。
要約すると、あまり意味がなく、悪い設計 (派生クラスとは異なる動作をする基本クラス) の匂いがし、非常に簡単に回避できるものが必要です。これらはすべて、実装すべきではない機能の兆候です。
だから、あなたは許可したい
class C { abstract void M(); }
コンパイルする。やったとしましょう。誰かがやったときに何をしたいですか
new C().M();
?実行時エラーが必要ですか?一般に、C#は実行時エラーよりもコンパイル時エラーを優先します。その哲学が気に入らない場合は、他の言語を利用できます...
あなた自身の質問に答えたと思います。抽象メソッドは最初は定義されていません。したがって、クラスをインスタンス化することはできません。あなたはそれを無視すべきだと言っていますが、抽象メソッドを追加するときの定義により、「これから作成されたすべてのクラスはこの{抽象メソッド}を実装する必要がある」と言っているため、抽象クラスを定義するクラスも抽象でなければなりません。その時点では、抽象メソッドはまだ定義されていません。
「仮想」メソッドを使用して目的を達成できますが、仮想メソッドを使用すると、子クラスにロジックを実装することが「強制」されないため、実行時のビジネス ロジック エラーが増える可能性があります。
ここには一理あると思います。抽象メソッドは、子でメソッド本体を定義するという要件を「強制」するため、完璧なソリューションです。
親クラスがいくつかのロジックを実装する必要がある (または実装した方が効率的) が、"子" のみが残りのロジックを実装できるという多くの状況に遭遇しました。
そのため、機会があれば、抽象的なメソッドと完全なメソッドを喜んで混ぜ合わせます。
@AakashM、C#がコンパイル時のエラーを好むことに感謝します。私もそうですし、誰でもそうです。これは、既成概念にとらわれずに考えることです。
これをサポートしても、それには影響しません。
ビッグボーイの決定に「万歳」と言うのではなく、ここで既成概念にとらわれずに考えてみましょう。
C# コンパイラは、"abstract" キーワードを使用しているため、抽象クラスを直接使用している人を検出して拒否できます。
C# は、子クラスに抽象メソッドを強制的に実装することも知っています。どのように?「abstract」キーワードを使用しているためです。
これは、プログラミング言語の内部構造を研究したことのある人なら誰でも理解するのは非常に簡単です。
では、なぜ C# は通常のクラスのメソッドの横にある "abstract" キーワードを検出できず、COMPILE TIME で処理できないのでしょうか。
その理由は、「手直し」が必要であり、小さな需要をサポートするのにその労力は割に合わないからです。
特に、大物が彼らに与えた枠にとらわれずに考える人が不足している業界では.
なぜそれが必要なのかはまだ明らかではありませんが、別のアプローチとして、派生クラスにデリゲート インスタンスを強制的に提供させることもできます。このようなもの
class MyConcreteClass
{
readonly Func<int, DateTime, string> methodImpl;
// constructor requires a delegate instance
public MyConcreteClass(Func<int, DateTime, string> methodImpl)
{
if (methodImpl == null)
throw new ArgumentNullException();
this.methodImpl = methodImpl;
}
...
}
(署名string MethodImpl(int, DateTime)
はもちろん一例です。)
それ以外の場合は、あなたの願いが世界をより良くするものではない理由を説明するために、他の回答をお勧めします.
OPが達成しようとしているものと非常によく似たシナリオがあります。私の場合、抽象化したいメソッドは保護されたメソッドであり、基本クラスにのみ認識されます。したがって、「新しい C().M();」問題のメソッドは公開されていないため、適用されません。基本クラスでパブリック メソッドをインスタンス化して呼び出すことができるようにしたい (したがって、非抽象である必要があります) が、子クラスで保護されたメソッドの保護された実装を呼び出すには、これらのパブリック メソッドが必要であり、デフォルトの実装はありません。親で。いわば、子孫に強制的にメソッドをオーバーライドさせる必要があります。依存性注入のため、コンパイル時に子クラスが何であるかわかりません。
私の解決策は、規則に従い、具象基本クラスと仮想保護メソッドを使用することでした。ただし、デフォルトの実装では、「子クラスの実装でメソッド名の実装を提供する必要があります」というエラーとともに NotImplementedException をスローします。
protected virtual void MyProtectedMethod()
{
throw new NotImplementedException("The implementation for MyProtectedMethod must be provided in the implementation of the child class.");
}
このように、デフォルトの実装は決して使用できず、子孫の実装の実装者は、重要なステップを逃したことにすぐに気付くでしょう。