オーバーライドしますが、呼び出しません
-
03-07-2019 - |
質問
dereivedクラスによってオーバーライドされる(またはオーバーライドされる)可能性のあるメソッドをC#で(どのようにアセンブリ外でも)宣言しますが、実際のクラス内からのみ呼び出し可能にする必要がありますか?
(つまり、C ++のプライベート仮想関数のような)
[編集]
private
virtual
はまさに私が意図したものです。"ここで私の動作を変更する方法がありますが、この関数を直接呼び出すことはできません(これを呼び出すには、基本クラスだけが行う不可解な呼び出しが必要なので)"
それを明確にするために、C#でそれを表現するのに最適なものは何ですか?
解決
C#は、「プライベート」をより強力に保証します。 C ++よりも。 C ++では、プライベート仮想メソッドを実際にオーバーライドできます。しかし、これは、基本クラスのコードが派生クラスのコードを実行できることを意味します。 privateメソッドは本当にprivateであり、同じクラスのメソッドによってのみ呼び出すことができるという約束を破ります。
ここで役に立たないのは、C ++が仮想キーワードを繰り返す必要がないことです。このようなリバースエンジニアリングが難しいミステリーに至るまで:
#include "stdafx.h"
#include <iostream>
class Base {
private:
virtual void Method() = 0;
public:
void Test() {
Method();
}
};
class Derived : public Base {
private:
void Method() { std::cout << "Who the heck called me?"; }
};
int _tmain(int argc, _TCHAR* argv[])
{
Base* p = new Derived;
p->Test();
}
私的継承には可能な役割があることに同意します。 C#言語のデザイナーは「いいえ」と言いました。しかし。
他のヒント
「実際のクラス内」でのみ呼び出し可能とする必要がある場合基本クラスと派生クラスのどちらですか?これらはどちらも単独では実行できません。最も近いのは、プロテクトメソッドを使用することです。つまり、宣言クラス、派生クラス、およびその他の派生クラスから呼び出すことができます。
プライベートメンバーは、子クラスには表示されません。保護された仮想はあなたが望むように機能すると思いますか?
更新:
ここでは、C#内で関数を継承してオーバーライドすることで何ができるかについて詳しく説明します。私はやや意味のある例を使用しようとしましたが、そのクラス設計が貧弱であることを理解しており、この方法で説明されているクラスの実装は推奨しません。ただし、これにより、元の問題を許容できる方法で解決する方法が得られることを願っています。具象クラスがそのメンバーのいずれかを呼び出すのを防ぐ方法はありませんが、構造がとにかくこのようなものである場合、おそらく問題ではありません。
public abstract class Animal
{
public void DisplayAttributes()
{
Console.WriteLine(Header());
Console.WriteLine("Name: " + Name());
Console.WriteLine("Legs: " + Legs());
Console.WriteLine();
}
protected virtual int Legs()
{
return 4;
}
private string Header()
{
return "Displaying Animal Attributes";
}
protected abstract string Name();
}
public class Bird : Animal
{
protected override string Name()
{
return "Bird";
}
protected override int Legs()
{
return 2;
}
}
public class Zebra : Animal
{
protected override string Name()
{
return "Zebra";
}
}
public class Fish : Animal
{
protected override string Name()
{
return "Fish";
}
protected override int Legs()
{
return 0;
}
private string Header()
{
return "Displaying Fish Attributes";
}
protected virtual int Gils()
{
return 2;
}
public new void DisplayAttributes()
{
Console.WriteLine(Header());
Console.WriteLine("Name: " + Name());
Console.WriteLine("Gils: " + Gils());
Console.WriteLine();
}
}
class Program
{
static void Main(string[] args)
{
Bird bird = new Bird();
bird.DisplayAttributes();
//Displaying Animal Attributes
//Name: Bird
//Legs: 2
Zebra zebra = new Zebra();
zebra.DisplayAttributes();
//Displaying Animal Attributes
//Name: Zebra
//Legs: 4
Fish fish = new Fish();
fish.DisplayAttributes();
//Displaying Fish Attributes
//Name: Fish
//Gils: 2
List<Animal> animalCollection = new List<Animal>();
animalCollection.Add(bird);
animalCollection.Add(zebra);
animalCollection.Add(fish);
foreach (Animal animal in animalCollection)
{
animal.DisplayAttributes();
//Displaying Animal Attributes
//Name: Bird
//Legs: 2
//Displaying Animal Attributes
//Name: Zebra
//Legs: 4
//Displaying Animal Attributes
//Name: Fish
//Legs: 0
//*Note the difference here
//Inheritted member cannot override the
//base class functionality of a non-virtual member
}
}
}
この例では、Bird、Zebra、およびFishはすべてNameメソッドとLegsメソッドを呼び出すことができますが、この例のコンテキスト内では、必ずしもそうすることの有用性はありません。さらに、Fishが示すように、DisplayAttributes()は具象派生クラスのインスタンス用に変更できます。しかし、foreachループのように動物を見ると、動物の実際のタイプに関係なく、基本クラスのDisplayAttributesの動作が得られます。これが、複製したい機能のタイプを理解するのに役立つことを願っています。
そのためにデリゲートを使用することを検討しましたか?派生クラスは、保護されたプロパティを介してデリゲートを設定したり、コンストラクタに渡すことができます。基本クラスのプライベートメソッドである内部実装へのデリゲートをデフォルトにすることもできます。
vboctorがすでに言及しているものの例を次に示します。
public class Base
{
private Func<Base, int> func;
protected void SetFunc(Func<Base, int> func)
{
this.func = func;
}
private void CallFunc()
{
if (func != null)
{
var i = func(this);
}
}
}
public class Sub : Base
{
private void DoFuncyStuff()
{
this.SetFunc(b => 42);
}
}
なぜプライベートにする必要があるのですか?ここでは、保護されていれば十分です。サブクラスの作成者に、呼び出せないコードを記述するよう依頼しています。これは何を達成しますか?とにかくそのコードを使用できます。
あなたの質問を読んだとき、あなたは二つのことを意味するかもしれません。
まず、子クラスBでオーバーライドできるが外部クラスには表示されないクラスAの関数が必要な場合:
public class ClassA
{
protected virtual ReturnType FunctionName(...) { ... }
}
public class ClassB
{
protected override ReturnType FunctionName(...) { ... }
}
2番目に、実装クラスに関数を定義させる場合:
public abstract class ClassA
{
protected abstract ReturnType FunctionName(...);
}
public class ClassB
{
protected override ReturnType FunctionName(...) { ... }
}
ちょっと関連しているC#を掘り下げている場合に見られるもう1つの概念は、部分クラスです。これは、コンパイル時に2つのソースファイルを結合して、同じアセンブリから1つのクラスを作成するという考え方です。
ファイル1:
public partial class ClassA
{
private ReturnType FunctionName(...);
}
ファイル2:
public partial class ClassA
{
//actual implimentation
private ReturnType FunctionName(...){ ... };
}
Linq2Sqlファイル、EDM、WinFormsなどの設計生成ファイルを処理する場合を除き、部分は広く使用されていません。
これは意図したとおりには動作しないと思いますが、擬似コードをいくつかスケッチしてみましょう。
public interface BaseClassFunction {
void PleaseCallMe();
}
public class BaseClass {
private BaseClassFunction fn;
public BaseClass(BaseClassFunction fn) {
this.fn = fn;
}
private CallMe() {
fn.PleaseCallMe();
}
public PublicCallMe() {
CallMe();
}
}
private class DerivedClassFunction : BaseClassFunction {
void PleaseCallMe() { ... do something important ... }
}
public class DerivedClassFunction {
public DerivedClassFunction() : BaseClass(new DerivedClassFunction()) {
}
}