465

なぜC#はこのように設計されたのですか?

私が理解しているように、インターフェースは動作を記述するだけであり、特定の動作が実装されるインターフェースを実装するクラスの契約上の義務を記述する目的を果たします。

クラスがその動作を共有メソッドに実装したい場合、なぜそうすべきではないのですか?

これが私が考えていることの例です:

// These items will be displayed in a list on the screen.
public interface IListItem {
  string ScreenName();
  ...
}

public class Animal: IListItem {
    // All animals will be called "Animal".
    public static string ScreenName() {
        return "Animal";
    }
....
}

public class Person: IListItem {

    private string name;

    // All persons will be called by their individual names.
    public string ScreenName() {
        return name;
    }

    ....

 }
4

27 に答える 27

227

これができない理由を尋ねていると仮定すると:

public interface IFoo {
    void Bar();
}

public class Foo: IFoo {
    public static void Bar() {}
}

これは意味的に意味がありません。インターフェイスで指定されたメソッドは、オブジェクトと対話するためのコントラクトを指定するために存在する必要があります。静的メソッドでは、オブジェクトと対話することはできません。実装を静的にすることができる立場にいることに気付いた場合は、そのメソッドが本当にインターフェイスに属しているかどうかを自問する必要があるかもしれません。


あなたの例を実装するには、アニマルに const プロパティを与えます。これにより、静的コンテキストからアクセスできるようになり、実装でその値が返されます。

public class Animal: IListItem {
    /* Can be tough to come up with a different, yet meaningful name!
     * A different casing convention, like Java has, would help here.
     */
    public const string AnimalScreenName = "Animal";
    public string ScreenName(){ return AnimalScreenName; }
}

より複雑な状況では、いつでも別の静的メソッドを宣言して、それに委譲することができます。例を考えてみると、静的コンテキストとインスタンス コンテキストの両方で自明ではないことを行う理由が思いつかなかったので、FooBar ブロブは割愛します。良い考えではありません。

于 2008-11-03T15:57:41.350 に答える
179

私の(単純化された)技術的な理由は、静的メソッドがvtableになく、呼び出しサイトがコンパイル時に選択されることです。オーバーライドまたは仮想静的メンバーを使用できないのと同じ理由です。詳細については、CS の卒業生またはコンパイラの専門家が必要です。私はそのどちらでもありません。

政治的な理由から、Eric Lippert (コンパイラの専門家であり、ウォータールー大学で数学、コンピューター サイエンス、応用数学の学士号を取得している (出典: LinkedIn ))の言葉を引用します。

...静的メソッドのコア設計原則、それらに名前を付ける原則...[である]...コンパイル時に、どのメソッドが呼び出されるかを常に正確に決定できます。つまり、メソッドは、コードの静的分析によってのみ解決できます。

Lippert は、いわゆる型メソッドの余地を残していることに注意してください。

つまり、(インスタンスや仮想とは異なり) null 非許容の「this」引数をとらない (静的のような) 型に関連付けられたメソッドですが、呼び出されるメソッドが T の構築された型に依存するメソッド (コンパイル時に決定可能でなければならない static とは異なります)。

しかし、その有用性についてはまだ確信が持てません。

于 2008-11-03T17:37:31.947 に答える
103

ここでのほとんどの回答は、要点を見逃しているようです。ポリモーフィズムは、インスタンス間だけでなく、型間でも使用できます。これは、ジェネリックを使用するときに必要になることがよくあります。

ジェネリック メソッドに型パラメーターがあり、それを操作する必要があるとします。コンストラクターを認識していないため、インスタンス化したくありません。

例えば:

Repository GetRepository<T>()
{
  //need to call T.IsQueryable, but can't!!!
  //need to call T.RowCount
  //need to call T.DoSomeStaticMath(int param)
}

...
var r = GetRepository<Customer>()

残念ながら、「醜い」代替案しか思いつきません。

  • 醜いリフレクションを使用し 、インターフェイスとポリモーフィズムのアイデアを打ち負かします。

  • 完全に別のファクトリ クラスを作成する

    これにより、コードの複雑さが大幅に増加する可能性があります。たとえば、ドメイン オブジェクトをモデル化しようとしている場合、各オブジェクトには別のリポジトリ クラスが必要になります。

  • インスタンス化してから、目的のインターフェイス メソッドを呼び出す

    これは、ジェネリック パラメーターとして使用されるクラスのソースを制御したとしても、実装が難しい場合があります。その理由は、たとえば、インスタンスがよく知られている「DB に接続されている」状態のみである必要がある場合があるためです。

例:

public class Customer 
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  void SomeOtherMethod() 
  { 
    //do work...
  }
}

インスタンス化を使用して静的インターフェースの問題を解決するには、次のことを行う必要があります。

public class Customer: IDoSomeStaticMath
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  //dummy instance
  public Customer() { IsDummy = true; }

  int DoSomeStaticMath(int a) { }

  void SomeOtherMethod() 
  { 
    if(!IsDummy) 
    {
      //do work...
    }
  }
}

これは明らかに見苦しく、また不必要なことで、他のすべてのメソッドのコードが複雑になります。明らかに、エレガントなソリューションでもありません!

于 2011-10-30T13:35:25.200 に答える
20

私はそれが古い質問であることを知っていますが、興味深いです。例は最善ではありません。使用例を示した方がはるかに明確になると思います:

文字列 DoSomething<T>() ここで T:ISomeFunction
{
  if (T.someFunction())
    ...
}

静的メソッドにインターフェイスを実装させるだけでは、目的を達成できません。必要なのは、インターフェイスの一部として静的メンバーを持つことです。特にものを作成できるようになると、そのための多くの使用例を確かに想像できます。私が提供できる2つのアプローチが役立つかもしれません:

  1. 型パラメーターが上記の DoSomething に渡す型になる静的ジェネリック クラスを作成します。このクラスの各バリエーションには、その型に関連するものを保持する 1 つ以上の静的メンバーがあります。この情報は、対象の各クラスに「情報登録」ルーチンを呼び出すか、クラス バリエーションの静的コンストラクターの実行時に Reflection を使用して情報を取得することによって提供できます。後者のアプローチは、Comparer<T>.Default() などで使用されていると思います。
  2. 対象のクラス T ごとに、IGetWhateverClassInfo<T> を実装し、「新しい」制約を満たすクラスまたは構造体を定義します。クラスには実際にはフィールドは含まれませんが、型情報を持つ静的フィールドを返す静的プロパティがあります。そのクラスまたは構造体の型を問題のジェネリック ルーチンに渡します。ジェネリック ルーチンはインスタンスを作成し、それを使用して他のクラスに関する情報を取得できます。この目的でクラスを使用する場合は、毎回新しい記述子オブジェクト インスタンスを作成する必要がないように、上記のように静的ジェネリック クラスを定義する必要があります。構造体を使用する場合、インスタンス化コストはゼロである必要がありますが、異なる構造体タイプごとに DoSomething ルーチンの異なる展開が必要になります。

これらのアプローチはどれも本当に魅力的ではありません。一方、この種の機能を明確に提供するメカニズムがCLRに存在する場合、.netではパラメーター化された「新しい」制約を指定できるようになると思います(クラスに特定のシグネチャを持つコンストラクターがあるかどうかを知ることは特定の署名を持つ静的メソッドがあるかどうかを知るのと同じくらい困難です)。

于 2011-11-17T17:05:45.480 に答える
16

近視だと思います。

最初に設計されたとき、インターフェイスはクラスのインスタンスでのみ使用されることを意図していました

IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();

ジェネリックの制約がインターフェイスに静的メソッドを追加することは実用的であるため、インターフェイスの導入のみでした。

(コメントへの返信:) 今変更するには、CLR の変更が必要になると思います。これにより、既存のアセンブリとの互換性が失われる可能性があります。

于 2008-11-03T15:46:44.743 に答える
15

インターフェースが「コントラクト」を表す限りにおいて、静的クラスがインターフェースを実装することは静かに合理的であるように思われます。

上記の議論はすべて、契約に関するこの点を見逃しているようです。

于 2012-08-22T03:47:47.043 に答える
14

インターフェイスは、オブジェクトの動作を指定します。

静的メソッドは、オブジェクトの動作を指定するのではなく、何らかの方法でオブジェクトに影響を与える動作を指定します。

于 2008-11-03T15:47:57.643 に答える
10

インターフェイスの目的はポリモーフィズムを許可することであるため、定義されたインターフェイスを実装するためにすべて定義されている定義済みのクラスのインスタンスをいくつでも渡すことができます...ポリモーフィック呼び出し内で、コードが見つけることができることを保証します呼び出しているメソッド。静的メソッドがインターフェイスを実装できるようにすることは意味がありません。

あなたはそれをどのように呼びますか??


public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
    public static void MyMethod() { //Do Something; }
}

 // inside of some other class ...  
 // How would you call the method on the interface ???
    MyClass.MyMethod();  // this calls the method normally 
                         // not through the interface...

    // This next fails you can't cast a classname to a different type... 
    // Only instances can be Cast to a different type...
    MyInterface myItf = MyClass as MyInterface;  
于 2008-11-03T17:51:07.987 に答える
4

非ジェネリック コンテキストで使用される静的メソッドに関しては、インターフェイスへの参照があった場合にそれらを呼び出すことができないため、インターフェイスでそれらを許可することはあまり意味がないことに同意します。ただし、多態的なコンテキストではなく、一般的なコンテキストでインターフェイスを使用することによって作成された言語設計には、根本的な穴があります。この場合、インターフェイスはインターフェイスではなく、制約です。C# にはインターフェイスの外側に制約の概念がないため、実質的な機能がありません。適例:

T SumElements<T>(T initVal, T[] values)
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

ここではポリモーフィズムはなく、ジェネリックはオブジェクトの実際の型を使用して += 演算子を呼び出しますが、その演算子が存在することを確認できないため、これは失敗します。簡単な解決策は、制約で指定することです。演算子は静的であり、静的メソッドはインターフェイスに含めることができず、(ここに問題があります) 制約はインターフェイスとして表されるため、単純な解決策は不可能です。

C# が必要とするのは実際の制約型です。すべてのインターフェイスも制約になりますが、すべての制約がインターフェイスになるわけではない場合、次のようにすることができます。

constraint CHasPlusEquals
{
    static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b);
}

T SumElements<T>(T initVal, T[] values) where T : CHasPlusEquals
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

すべての数値型を実装するために IArithmetic を作成することについてはすでに多くの議論がありましたが、制約はポリモーフィック コンストラクトではないため、効率に関する懸念があります。CArithmetic 制約を作成すると、その問題が解決されます。

于 2013-08-13T17:26:04.293 に答える
3

インターフェイスは継承構造であり、静的メソッドはうまく継承されないためです。

于 2008-11-03T15:43:44.220 に答える
3

クラスの静的メソッドと非静的メソッドは、異なるインターフェイスと考えることができます。呼び出されると、静的メソッドは単一の静的クラス オブジェクトに解決され、非静的メソッドは処理するクラスのインスタンスに解決されます。したがって、インターフェイスで静的メソッドと非静的メソッドを使用する場合、実際にはインターフェイスを使用して 1 つのまとまりのあるものにアクセスしたい場合に、実質的に 2 つのインターフェイスを宣言することになります。

于 2008-11-03T16:49:06.147 に答える
3

インターフェイスメソッドの静的実装またはMark Brackettが「いわゆる型メソッド」として導入したもののいずれかが欠けている例を挙げると:

データベース ストレージから読み取る場合、任意の構造のテーブルからの読み取りを処理するジェネリック DataTable クラスがあります。すべてのテーブル固有の情報は、DB からの 1 行のデータも保持し、IDataRow インターフェイスを実装する必要があるテーブルごとに 1 つのクラスに配置されます。IDataRow には、データベースから読み取るテーブルの構造の説明が含まれています。DataTable は、DB から読み取る前に、IDataRow からデータ構造を要求する必要があります。現在、これは次のようになっています。

interface IDataRow {
  string GetDataSTructre();  // How to read data from the DB
  void Read(IDBDataRow);     // How to populate this datarow from DB data
}

public class DataTable<T> : List<T> where T : IDataRow {

  public string GetDataStructure()
    // Desired: Static or Type method:
    // return (T.GetDataStructure());
    // Required: Instantiate a new class:
    return (new T().GetDataStructure());
  }

}

GetDataStructure は、各テーブルを読み取るために 1 回だけ必要です。もう 1 つのインスタンスをインスタンス化するためのオーバーヘッドは最小限です。ただ、こちらの場合はこれでよろしいかと。

于 2009-02-26T07:16:06.917 に答える
3

あなたが望むように見えるのは、静的メソッドを Type またはその型のインスタンスの両方を介して呼び出すことができるようにすることです。これは、少なくとも望ましい特性ではないあいまいさをもたらします。

それが重要であるかどうか、これがベスト プラクティスであるかどうか、および何らかの方法で実行するとパフォーマンスの問題があるかどうかについて、際限のない議論が行われるでしょう。C# をサポートしないだけで、心配する必要がなくなります。

また、この欲求に適合したコンパイラーは、インスタンスと静的メソッドをより厳密に分離することで、いくつかの最適化を失う可能性があります。

于 2008-11-03T16:33:18.050 に答える
1

参考までに: インターフェイスの拡張メソッドを作成することで、必要な動作と同様の動作を得ることができます。拡張メソッドは、共有され、オーバーライドできない静的な動作になります。ただし、残念ながら、この静的メソッドはコントラクトの一部ではありません。

于 2008-11-03T16:46:39.583 に答える
1

静的クラスは、汎用的に使用できるように、これを実行できる必要があります。目的の結果を得るには、代わりにシングルトンを実装する必要がありました。

「ユーザー」、「チーム」などのエンティティ タイプごとに、「作成」、「読み取り」、「更新」、「削除」などの CRUD メソッドを実装する一連の静的ビジネス レイヤー クラスがありました。次に、ベースを作成しました。 CRUD メソッドを実装したビジネス層クラスの抽象プロパティを持つコントロール。これにより、基本クラスからの「作成」、「読み取り」、「更新」、「削除」操作を自動化できました。静的制限のため、シングルトンを使用する必要がありました。

于 2011-01-19T17:18:36.203 に答える
1

ほとんどの人は、OOP ではクラスもオブジェクトであることを忘れているようで、何らかの理由で c# が「静的メソッド」を呼び出すメッセージがあります。インスタンス オブジェクトとクラス オブジェクトの間に違いがあるという事実は、言語の欠陥または欠点を示すだけです。ただし、C#については楽観的です...

于 2013-07-15T18:55:42.133 に答える
1

インターフェイスは、定義された使用可能な機能の抽象的なセットです。

そのインターフェイスのメソッドが static として動作するかどうかは、インターフェイスの背後に隠されるべき実装の詳細です。メソッドを特定の方法で実装することを不必要に強制することになるため、インターフェイス メソッドを静的として定義するのは間違っています。

メソッドが静的として定義されている場合、インターフェイスを実装するクラスは可能な限りカプセル化されません。カプセル化は、オブジェクト指向設計で努力するのに適しています (理由については説明しません。http: //en.wikipedia.org/wiki/Object-directionalで読むことができます)。このため、静的メソッドはインターフェイスでは許可されていません。

于 2008-11-03T16:34:26.380 に答える
1

C# と CLR は、Java と同様にインターフェイスで静的メソッドをサポートする必要があります。static 修飾子はコントラクト定義の一部であり、具体的には動作と戻り値がインスタンスごとに異なることはありませんが、呼び出しごとに異なる可能性があるという意味があります。

とはいえ、インターフェイスで静的メソッドを使用したいができない場合は、代わりにアノテーションを使用することをお勧めします。求めていた機能を手に入れることができます。

于 2016-01-18T22:33:03.703 に答える
1

The fact that a static class is implemented in C# by Microsoft creating a special instance of a class with the static elements is just an oddity of how static functionality is achieved. It is isn't a theoretical point.

An interface SHOULD be a descriptor of the class interface - or how it is interacted with, and that should include interactions that are static. The general definition of interface (from Meriam-Webster): the place or area at which different things meet and communicate with or affect each other. When you omit static components of a class or static classes entirely, we are ignoring large sections of how these bad boys interact.

Here is a very clear example of where being able to use interfaces with static classes would be quite useful:

public interface ICrudModel<T, Tk>
{
    Boolean Create(T obj);
    T Retrieve(Tk key);
    Boolean Update(T obj);
    Boolean Delete(T obj);
}

Currently, I write the static classes that contain these methods without any kind of checking to make sure that I haven't forgotten anything. Is like the bad old days of programming before OOP.

于 2015-07-26T00:22:33.547 に答える
0

短い答えは「役に立たないから」だと思います。インターフェイス メソッドを呼び出すには、型のインスタンスが必要です。インスタンス メソッドから、任意の静的メソッドを呼び出すことができます。

于 2008-11-03T17:29:33.320 に答える
0

実際、そうなります(または、少なくともそうなるでしょう)

2022 年 1 月の時点で、C# および .NET の今後のバージョンでは、いわゆるstatic abstractメンバーが完全にサポートされます。

interface INumber<T>
{
    static abstract T Zero { get; }
}

struct Fraction : INumber<Fraction>
{
    public static Fraction Zero { get; } = new Fraction();

    public long Numerator;
    public ulong Denominator;

    ....
}
于 2022-01-30T09:23:54.200 に答える
0

オブジェクト指向の概念に従って、クラスによって実装されたインターフェイスであり、オブジェクトを使用してこれらの実装された関数 (またはメソッド) にアクセスするための契約があります。

したがって、Interface Contract メソッドにアクセスする場合は、オブジェクトを作成する必要があります。静的メソッドの場合、許可されないのは常に必須です。静的クラス、メソッド、および変数は、オブジェクトを必要とせず、その領域(またはクラス)のオブジェクトを作成せずにメモリにロードするか、オブジェクトの作成を必要としないと言えます。

于 2017-07-02T13:31:49.717 に答える