web-dev-qa-db-ja.com

なぜインターフェースにキャストするのですか?

Jesse LibertyのプログラミングC#(p.142)で、オブジェクトをインターフェイスにキャストする例を示しています。

 interface IStorable
 {
    ...
 }

 public class Document : IStorable
 {
    ...
 }

 ...
 IStorable isDoc = (IStorable) doc;  
 ...

これのポイントは何ですか、特にオブジェクトのクラスがとにかくインターフェースを実装している場合はどうでしょうか?

EDIT1:明確にするために、私はキャストの理由(ある場合)ではない実装の理由に興味がありますインターフェース。また、本は彼の2001初版です(C#1に基づいているため、この例はそれ以降のバージョンのC#には適さない場合があります)。

EDIT2:私はコードにいくつかのコンテキストを追加しました

37
eft

実際にキャストが必要な理由は1つだけです。docがIStorableを実装する実際のオブジェクトの基本型である場合です。説明させてください:

public class DocBase
{
  public virtual void DoSomething()
  {

  }
}

public class Document : DocBase, IStorable
{
  public override void DoSomething()
  {
    // Some implementation
    base.DoSomething();
  }

  #region IStorable Members

  public void Store()
  {
    // Implement this one aswell..
    throw new NotImplementedException();
  }

  #endregion
}

public class Program
{
  static void Main()
  {
    DocBase doc = new Document();
    // Now you will need a cast to reach IStorable members
    IStorable storable = (IStorable)doc;
  }
}

public interface IStorable
{
  void Store();
}
18
Jeroen Landheer

インターフェースによって提供されるメソッドのみに制限したいからです。クラスを使用する場合、インターフェースの一部ではないメソッドを(偶然に)呼び出すリスクがあります。

21
Tundey

オブジェクトが明示的にインターフェイスを実装している場合(public void IStorable.StoreThis(...))、キャストは、実際にインターフェイスメンバーに到達する最も簡単な方法です。

15
James Curran

本の例がどのような文脈で与えられたかはわかりません。ただし、通常、オブジェクトを型キャストしてインターフェイスに多重継承することができます。以下の例を挙げました。

public interface IFoo
{
     void Display();
}
public interface IBar
{
     void Display();
}

public class MyClass : IFoo, IBar
{
    void IBar.Display()
    {
        Console.WriteLine("IBar implementation");
    }
    void IFoo.Display()
    {
        Console.WriteLine("IFoo implementation");
    }
}

public static void Main()
{
    MyClass c = new MyClass();
    IBar b = c as IBar;
    IFoo f = c as IFoo;
    b.Display();
    f.Display();
    Console.ReadLine();
}

これは表示されます

IBarの実装
IFooの実装

13
Ramesh

これ以上のコンテキストがないとわかりにくいです。変数docがインターフェイスを実装する型であると宣言されている場合、キャストは冗長です。

読んでいる本のバージョンはどれですか。 「Programming C#3.0」の場合は、今夜、家にいるときに確認します。

編集:これまでの回答で見たように、ここには3つの潜在的な質問があります:

  • 質問に示されたステートメントをキャストするのはなぜですか? (回答:docが適切なコンパイル時の型である場合は必要ありません)
  • 実装されたインターフェイスまたは基本クラスに明示的にキャストすることが適切である理由は何ですか? (回答:別の回答に示されている明示的なインターフェースの実装、およびキャスト値を引数として渡すときに、あまり具体的でないオーバーロードを選択するためにも。)
  • なぜインターフェースを使用するのですか? (回答:インターフェースタイプを使用すると、後で具象タイプを変更しても影響を受けにくくなります。)
10
Jon Skeet

docオブジェクトは、IStorableのメンバーを明示的に実装するタイプであり、クラスのプライマリインターフェースに追加しないでください(つまり、インターフェースを介してのみ呼び出すことができます)。

C#は(たとえばF#とは異なり)アップキャスト(親型へのキャスト)を自動的に処理するため、実際には(キャスト)((T)構文を使用)は意味をなしません。

3

ここには良い答えがたくさんありますが、可能な限り最も制限の厳しいインターフェースを使用したいのはなぜでしょうか。

理由には、最初のコーディングは含まれません。次にコードにアクセスしたり、コードをリファクタリングしたりするとき、または誰かがそれを行うときがその理由です。

ボタンが必要で、それを画面に配置するとします。次のように、別の関数からまたは別の関数からボタンを取得しています。

Button x=otherObject.getVisibleThingy();
frame.add(x);

VisibleThingyはボタンであり、ボタンを返すため、ここではすべてがクールです(キャストは必要ありません)。

ここで、VisibleThingyをリファクタリングして、代わりにトグルボタンを返すとします。実装についてよく知っているので、メソッドをリファクタリングする必要があります。

次のように最初の行を記述した場合、Componentのメソッド(ボタンとToggleの両方の親であり、インターフェースである可能性があるため、ほとんど同じです)が必要なので、

Component x=(Component)otherObject.getVisibleThingy();

あなたは何もリファクタリングする必要がなかったでしょう-それはうまくいったでしょう。

これは非常に単純なケースですが、はるかに複雑になる可能性があります。

つまり、インターフェイスはオブジェクトを「表示」するための特定の方法であり、フィルターを介してそれを見るようなものだと思います。一部しか表示されません。ビューを十分に制限できる場合、オブジェクトは特定のビューの背後で「モーフ」し、現在の世界では何も影響を与えません。これは、非常に強力な抽象化のトリックです。

2
Bill K

インターフェースにキャストする最大の理由は、オブジェクトに対してコードを記述していて、それらがどの具象型であるかわからないため、したくない場合です。

特定のインターフェイスを実装するオブジェクトに遭遇する可能性があることがわかっている場合は、このオブジェクトの具象クラスを知らなくても、オブジェクトから値を取得できます。また、オブジェクトが特定のインターフェースを実装していることがわかっている場合、そのインターフェースは、オブジェクトに対して特定のアクションを実行するために実行できるメソッドを定義する場合があります。

以下に簡単な例を示します。

public interface IText
{
   string Text { get; }
}

public interface ISuperDooper
{
   string WhyAmISuperDooper { get; }
}

public class Control
{
   public int ID { get; set; }
}

public class TextControl : Control, IText
{
   public string Text { get; set; }
}

public class AnotherTextControl : Control, IText
{
   public string Text { get; set; }
}

public class SuperDooperControl : Control, ISuperDooper
{
   public string WhyAmISuperDooper { get; set; }
}

public class TestProgram
{
   static void Main(string[] args)
   {
      List<Control> controls = new List<Control>
               {
                   new TextControl
                       {
                           ID = 1, 
                           Text = "I'm a text control"
                       },
                   new AnotherTextControl
                       {
                           ID = 2, 
                           Text = "I'm another text control"
                       },
                   new SuperDooperControl
                       {
                           ID = 3, 
                           WhyAmISuperDooper = "Just Because"
                       }
               };

       DoSomething(controls);
   }

   static void DoSomething(List<Control> controls)
   {
      foreach(Control control in controls)
      {
         // write out the ID of the control
         Console.WriteLine("ID: {0}", control.ID);

         // if this control is a Text control, get the text value from it.
         if (control is IText)
            Console.WriteLine("Text: {0}", ((IText)control).Text);

         // if this control is a SuperDooperControl control, get why
         if (control is ISuperDooper)
            Console.WriteLine("Text: {0}", 
                ((ISuperDooper)control).WhyAmISuperDooper);
      }
   }
}

この小さなプログラムを実行すると、次の出力が得られます。

ID:1

テキスト:私はテキストコントロールです

ID:2

テキスト:私は別のテキストコントロールです

ID:3

テキスト:理由

DoSomethingメソッドでコードを記述する必要がなかったので、具体的なオブジェクト型として取り組んでいるすべてのオブジェクトについて知っておく必要があることに注意してください。私が知っている唯一のことは、少なくともControlクラスのインスタンスであるオブジェクトで作業していることです。次に、インターフェイスを使用して、他に何があるかを調べます。

オブジェクトのインターフェースでこのアプローチをとる理由は100万通りありますが、それが何であるかを正確に知る必要なく、オブジェクトにアクセスするための緩い方法を提供します。

世界のすべてのクレジットカードを考えてみてください。すべての会社が独自に作成しますが、インターフェイスは同じです。したがって、すべてのカードリーダーが標準に従ってカードをスワイプできます。インターフェースの使用法に似ています。

2
Dean Poulin

すでに述べたように、キャスティングは不必要であり、必要ありません。ただし、これはコーディングのより明確な形式であり、初心者が理解を助けるのに役立ちます。

入門用の教科書では、初心者にとって混乱しやすいように、コンパイラに暗黙のうちに何かをさせるのではなく、明示的に行動することが最善です。

「doc」は「IStorable」タイプではないため、初心者がisDocに割り当てられていることを確認するのは混乱します。 (本とコードの)作成者は、明示的にキャストすることにより、ドキュメントをIStorableオブジェクトにキャストできると述べていますが、IStorableオブジェクトと同じではありません。

1
DevinB

コードの断片間の最も分離を可能にするために...

詳細については、次の記事を参照してください。 Interfaces

0
Tom Deleu

ポイントは、オブジェクト(どこで入手したのか?) ない インターフェースを実装します。その場合、キャッチして処理できる例外がスローされます。もちろん、 "is"演算子を使用してチェックし、 "as"演算子を使用してCスタイルのキャストの代わりにキャストできます。

0
royatl