質問
Jesse LibertyのプログラミングC#(p.142)では、インターフェイスにオブジェクトをキャストする例を提供しています。
interface IStorable
{
...
}
public class Document : IStorable
{
...
}
...
IStorable isDoc = (IStorable) doc;
...
特にオブジェクトのクラスがインターフェイスを実装している場合、これのポイントは何ですか?
編集1:明確にするために、キャストの理由(ある場合)に興味があります。インターフェースを実装する理由ではありません。 また、この本は彼の2001年初版です(C#1に基づいているため、この例はC#の以降のバージョンでは適切ではありません)。
EDIT2:コードにコンテキストを追加しました
解決
実際にキャストが必要な理由は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();
}
他のヒント
インターフェイスで提供されるメソッドのみに制限するため。クラスを使用すると、インターフェイスの一部ではないメソッドを(誤って)呼び出すリスクが発生します。
オブジェクトがインターフェイスを明示的に実装している場合( public void IStorable.StoreThis(...)
)、キャストが実際にインターフェイスメンバーに到達する最も簡単な方法です。
本で例がどのような文脈で与えられたかはわかりません。ただし、通常、オブジェクトをインターフェイスに型キャストして、多重継承を実現できます。以下に例を示しました。
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の実装
これ以上のコンテキストなしで伝えるのはかなり難しいです。変数 doc
がインターフェイスを実装する型として宣言されている場合、キャストは冗長です。
どのバージョンの本を読んでいますか? 「Programming C#3.0」の場合今夜は家にいるときに見ます。
編集:これまでの回答で見てきたように、ここには3つの潜在的な質問があります:
- 質問に示されているステートメントをキャストする理由(回答:
doc
が適切なコンパイル時タイプである場合は必要ありません) - 実装されたインターフェースまたは基本クラスに明示的にキャストすることが適切なのはなぜですか? (回答:別の回答に示されている明示的なインターフェイスの実装、およびキャスト値を引数として渡すときに特定性の低いオーバーロードを選択するため。)
- インターフェースを使用する理由(回答:インターフェイスタイプを使用することで、後で具象タイプの変更の影響を受けにくくなります。)
doc
オブジェクトは、 IStorable
のメンバーをクラスのプライマリインターフェイスに追加せずに明示的に実装するタイプの場合があります(つまり、インターフェイス)。
実際には「キャスティング」 ((T)構文を使用)は、C#が(たとえばF#とは異なり)アップキャスト(親タイプへのキャスト)を自動的に処理するため、意味がありません。
ここには多くの良い答えがありますが、可能な限り最も制限的なインターフェースを使用したいのはなぜだと思うとは思いません。
理由は最初のコーディングではなく、次回コードを訪問またはリファクタリングするとき、または他の誰かがそれを行うときです。
ボタンが必要で、画面に配置しているとしましょう。次のように、別の関数に渡されるか、別の関数からボタンを取得しています:
Button x=otherObject.getVisibleThingy();
frame.add(x);
VisibleThingyはボタンであり、ボタンを返すことを知っているので、ここではすべてがクールです(キャストは不要です)。
今、VisibleThingyをリファクタリングして、代わりにトグルボタンを返すとしましょう。実装についてよく知っているので、メソッドをリファクタリングする必要があります。
最初の行を次のように書いた場合は、コンポーネントのメソッド(ボタンとトグルの両方の親である可能性があります。これらはインターフェイスである可能性があります。 >
Component x=(Component)otherObject.getVisibleThingy();
何もリファクタリングする必要はありませんでした-それはちょうど働いていたでしょう。
これは非常に単純なケースですが、さらに複雑になる可能性があります。
だから、インターフェイスは「表示」するための特定の方法であるということだと思います。あなたのオブジェクト-フィルターを通してそれを見るような...あなたはいくつかの部分しか見ることができません。ビューを十分に制限できる場合、オブジェクトは" Morph"を実行できます。特定のビューの背後にあり、現在の世界に影響を与えない-非常に強力な抽象化のトリック。
インターフェイスにキャストする最大の理由は、オブジェクトに対してコードを記述していて、オブジェクトの具体的な型がわからないため、そうしたくない場合です。
特定のインターフェイスを実装するオブジェクトに出くわす可能性があることがわかっている場合、このオブジェクトの具象クラスを知らなくても、オブジェクトから値を取得できます。また、オブジェクトが特定のインターフェイスを実装することがわかっている場合、そのインターフェイスは、オブジェクトに対して特定のアクションを実行するために実行できるメソッドを定義する場合があります。
簡単な例を次に示します。
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クラスのインスタンスであるオブジェクトで作業しているということです。その後、インターフェイスを使用して、他に何があるかを確認できます。
オブジェクトのインターフェイスでこのアプローチを採用する理由は数百通りありますが、それが何であるかを正確に知らなくてもオブジェクトにアクセスするための緩い方法を提供します。
世界中のすべてのクレジットカードを考えてみてください。すべての会社が独自に作っていますが、インターフェイスは同じです。したがって、すべてのカードリーダーは、標準に従ってカードをスワイプできます。インターフェイスの使用法に似ています。
前述のように、鋳造は不要であり、必要ありません。ただし、初心者が理解を支援するのに役立つコーディングのより明示的な形式です。
入門的な教科書では、暗黙のうちにコンパイラーに物事を行わせるのではなく、明示的に行動するのが最善です。
&quot; doc&quot;タイプが「IStorable」ではありません。そのため、初心者がisDocに割り当てられていることを確認するのはわかりにくいでしょう。明示的にキャストすることにより、(本およびコードの)著者は、ドキュメントをIStorableオブジェクトにキャストできると言っていますが、それはIStorableオブジェクトと同じではありません。
重要なのは、オブジェクト(どこで取得したのか)がインターフェイスを実装していない可能性があることです。その場合、キャッチして処理できる例外がスローされます。もちろん、「is」を使用できます。チェックする演算子、および「as」 Cスタイルのキャストの代わりにキャストする演算子。
コードの断片間の分離を最大限にするために...
詳細については、次の記事を参照してください。 インターフェース