3

C# はどのようにポリモーフィックなのだろうか?

ポリモーフィズムという言葉の定義に基づく場合( ...オブジェクト指向プログラミングの文脈におけるポリモーフィズムは、変数、関数、または複数の形式を持つオブジェクトを作成する能力です ... )、もちろん、ポリモーフィズムについてより多くの決定を下すことはできますが、平均して、さまざまな単語を使用して、コンピューター サイエンスにおけるポリモーフィズムとは何かを理解していると思います。

それで、私がC#に関連する私の質問について。

たとえば、私は非常に単純で単純な例を使用しています。これは、あるクラスの標準的な仮想メソッドAと、他のクラスのオーバーライド メソッドBです。

using System;


class A
{
    public virtual void CallMe(string incString)
    {
        Console.WriteLine(incString);
    }
}

class B : A
{
    public override void CallMe(string incString)
    {
        incString += "_new";
        Console.WriteLine(incString);
    }
}

class App
{
    static void Main()
    {
        B objectB = new B();
        objectB.CallMe("hello");
    }
}

まず第一に、私は腹を立てました: C# では、メソッドの戻り値の型と引数の両方のオーバーライドを使用できるようになったのはなぜですか?

たとえば、設定するだけです:

public override int CallMe(string incString)

しかし、良い結果は得られず、コンパイラの例外をキャッチします:

B.CallMe(string)': return type must be `void' to match overridden member

私が説明した状況ではnew、似たような名前のメソッドを定義したいが、戻り値の型/引数のリストが異なる場合、キーワードを使用してメソッドを非表示にすることによってのみ、C# で解決できます。認めたいのですが、それを隠す場合、それはポリモーフィズムではなく、単なる隠蔽プロセスです。ポリモーフィズムの定義では、既存のものをオーバーライドする必要があります (同様のプロセスは、化学、数学などの他の科学にも見られます...)

たとえば、数学では、既存のセットまたはオブジェクトを常に再定義できる 2 番目、3 番目の... N-arny オーダーのポリモーフィズムを使用できます。しかし、コンピューター サイエンスと C# は正確にはどうでしょうか。

一部の関数型プログラミング言語 (LISP、Haskell) でそのような機能が許可されていることを読んだことがありますが、試していません。

新しいバージョンごとにC#が機能的なものを増やしているのを見ると、ラムダ/デリゲートで可能になるので、将来C#で見ることは可能ですか?

あなたはそれについてどう思いますか?

4

5 に答える 5

2

オブジェクト指向プログラミングにおけるポリモーフィズムのポイントは、異なることを行うメソッドを持つ異なるクラスを持つことができますが、それらを同じ方法で呼び出すことができるということです。したがって、オーバーライドはまったく同じメソッド シグネチャで行われます。シグネチャを変更すると、同じ方法でメソッドを使用できなくなるためです。

メソッド シグネチャが同じままであるため、基本クラス (またはインターフェイス) への参照を使用するコードと、その型を実装する任意のクラスである実際のインスタンスを使用できます。コードは、実際の型を気にせずに同じ呼び出しを行うことができます。

例:

A obj;
if (new Random().Next(2) == 0) {
  obj = new A();
} else {
  obj = new B();
}
obj.CallMe("hello");
于 2013-04-30T08:33:03.850 に答える
2

オーバーライドオーバーロードを混同していると思います。同じシグネチャを持つメソッドに異なる動作を与えたい場合は、オーバーライドが適用されます。

一方、オーバーロードは、同じ名前のメソッドを作成する方法ですが、署名は異なります。この 2 つの概念を混ぜ合わせると大変なことになると思います。

将来的にC#で見ることは可能ですか

いいえ、それが不可能であることを願っています。少なくとも、これは重大な変更になるためです。

UPD

あなたが望むことが可能であると考えてみましょう。これらのクラスを見てください:

class A
{
    public virtual int CallMe(string incString)
    {
        // some code    
    }
}

class B : A
{
    public override void CallMe(string incString)
    {
        // some code
    }
}

// somewhere in the code (e.g., host application, which loads a plugin)
var a = serviceLocator.GetService<A>(); // indeed returns `B` instance 
var result = a.CallMe(); // hey, stop! B.CallMe is void. WTF?

のみを知っているコードは、実行時ではなく を受け取るAときにどのように動作する必要がありますか?AB

于 2013-04-30T08:25:48.257 に答える
2

メソッドを作成するときvirtual/abstractは、基本クラスとすべての可能性のある将来の派生クラスとの間の契約を定義しています。virtual/abstract派生クラスは、メソッドが暗示する正確なシグネチャを含むメソッドを提供することによって、この契約に「同意」する必要があります。
まさにその契約を破った場合、ポリモーフィズムはどのように機能すると思いますか? それはまったくうまくいきません!コレクションを介して列挙するように実装できる を
検討してください。interfaceIEnumerable

MSDNから

これには method が含まれています。署名を好きなように変更できる場合、オブジェクトに依存して正当なメソッドを提供できないbool MoveNext()ため、フレームワーク全体が壊れてしまいます。foreachMoveNext()

再びMSDNから

オーバーライド メソッドは、基本クラスから継承されたメンバーの新しい実装を提供します。オーバーライド宣言によってオーバーライドされるメソッドは、オーバーライドされた基本メソッドと呼ばれます。オーバーライドされた基本メソッドには、オーバーライド メソッドと同じ署名が必要です。継承の詳細については、「継承 (C# プログラミング ガイド)」を参照してください。

于 2013-04-30T08:26:08.293 に答える
1

ルール的には、あなたのケースはそれほど大げさではありません。概念的には、何も返さない代わりに何かを返すことはユースケースを壊さないからです...概念的に。

ただし、いくつかの異なる要因が関係しています。まず、C# は通常、メソッドの有用な出力が戻り値として返されるようにプログラムされています。あなたが提案したように、ポリモーフィズムを使用して avoidを aに変更すると、基本クラスのユーザーに意味のある出力があることを無視するように強制されます。これはせいぜい悪い設計の兆候です。string

とはいえ、このケースはおそらく少し極端であり、異なるサイズの型を返す必要があります (これにより、言語全体を遅くすることによってのみ修正できる技術的な問題が発生する可能性もあります)。使用できるその概念のより穏やかな実装があります。objectの代わりに、より緩い型をオーバーライドに受け入れるか、最初の例の代わりにstring、より正確な型を返すようにすることが可能です。それは共分散と反分散と呼ばれます。参照はすべて同じサイズであるため、これらは技術的な問題を引き起こすことはありません。確認する必要がありますが、これはJavaでも可能だと思います。私は、Objective-C の型を返すか受け入れるブロックに対して、Objective-C でそれが可能であることを知っています。BA

私の知る限り、C# メソッドのオーバーライドには至っていません。おそらく、Microsoft が機能の開発、テスト、および保守のコストが利点を上回っていると判断したためです。

ただし、補足として、一般的な引数にしました。ジェネリック型を宣言するとき、out「これまたはサブクラス」を意味する で型に注釈を付けることができます。たとえば、IEnumerable は次のように定義されIEnumerable<out T>ます。これは、 anyIEnumerable<string>も であることを意味しますIEnumerable<object>

于 2013-04-30T18:22:14.260 に答える
1

ポリモーフィックの原則

コンピュータ サイエンスの多くのトピックでは、一定の厳密さが求められます。ポリモーフィズムも例外ではありません。戻り値の型を共有するという要件は重要であり、多くの時間を節約し、強力な仮定を行うことができます。同じ出力を持たないメソッドはポリモーフィックではありません (他の言語がこのプロセスを悪用するのは、おそらく、ラムダや C# が持つ他の優れた推論機能の一部を使用できない理由です)。

現状では、戻り値の型に基づいてどのメソッドを呼び出すかを推測することはできません。The signature of a method does not include the return type.


ベストプラクティスに固執する

真のポリモーフィック原則を採用した読みやすいコードを書くことが最善です。

以下のプロセスがいかに読みにくくなるかがわかります。より良い方法は、懸念を分離することです(Comp Sci のもう 1 つの大きなトピック)。この int またはこれらの戻り値で実際に行われていること。これらはおそらくロジックのフォークを表しているため、これらのケースではロジックが異なることを反映するために、おそらく異なるシグネチャで定義する必要があります。


コンパイラは戻り値の型を推測できません

同じシグネチャを持つが異なる型を返す 2 つの定義がある場合、どちらを使用すべきかを判断する方法がなく、コンパイラは「メンバーが同じパラメーターで既に定義されている」という行に沿ってエラーをスローします。種類"。

以下は、異なる戻り値の型を使用することが良くない理由の例です。これがコンパイルされると仮定しましょう

public override string CallMe(string incString)
{
    incString += "_new";
    return incString;
}

public override int CallMe(string incString)
{
    incString += "_new";
return 1;
}

コンパイラは、推論の状況でどちらを使用するかをどのように判断しますか?

object o = CallMe("hello");
var v = CallMe("hello");
Type t = CallMe("hello").GetType();

ジェネリックはこれを行うことができます

ジェネリックは C# の強力なツールです。あなたの例では、ジェネリックを使用して簡単に解決できます。ただし、これには批判されることもあるタイプの切り替えが必要になります。

class A{public virtual void CallMe(string incString){Console.WriteLine(incString);}}

class B : A
{
 public override void CallMe(string incString)
 {
    incString += "_new";
    Console.WriteLine(incString);
 }

 public T CallMe<T>(string incString)
 {
    CallMe(incString);
    object o;
    switch( typeof(T).ToString() ){
        case "System.String":
            o = incString;
            break;
        case "System.Int32":
            o = 1;
            break;
        default:
            o = Activator.CreateInstance<T>();
            break;
    }
    return (T)o;
 }
}

これは次のように使用されます。

static void Main()
{
 B objectB = new B();
 objectB.CallMe("hello");//writes "hello_new"
 int i = objectB.CallMe<int>("generic");//writes "generic_new"
 string s = objectB.CallMe<string>("world");//writes "world_new"
 A a = objectB.CallMe<A>("!");//writes "!_new"
 Console.WriteLine(i);//1
 Console.WriteLine(s);//"world"
 Console.WriteLine(a);//new A()
}

結論

C# は非常にポリモーフィックですが、誰にとっても難しいコードを使用することを避け、構造化されたアプローチを取ることが重要です。何を期待するかについてより強力な仮定を立てることができるように、予測可能な一意の署名を持っています。

明らかに、このコードにはあまりにも多くの分野横断的な懸念があり始めており、責任が多すぎてリファクタリングが切実に必要になっています。

私の意見では、C# はコーディングに最適な言語です。これは、ベスト プラクティスの実施に関して骨の折れる方法で設計されていることが主な原因です。

于 2013-04-30T20:22:24.417 に答える