質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

6417閲覧

インターフェイスに変換したい

aglkjggg

総合スコア769

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2017/02/01 03:06

編集2017/02/01 14:29

Ⅰ. 前提

現在3つのクラスとインターフェイスがあります。

  1. IProduct

商品について必要最低限を定義したインターフェイス
2. AmazonProduct
Amazonの商品について定義したクラス(IProductを継承)
3. EbayProduct
Ebayの商品について定義したクラス(IProductを継承)

C#

1interface IProduct 2{ 3 string Name { get; set; } 4 int Price { get; set; } 5} 6 7public class AmazonProduct : IProduct 8{ 9 public string Asin { get; set; } // Amazonグループだけが取り扱う特殊な番号 10 public string Name { get; set; } 11 public int Price { get; set; } 12} 13 14public class EbayProduct : IProduct 15{ 16 public Country OwnerCountry { get; set; } // 出品者の国 17 public string Name { get; set; } 18 public int Price { get; set; } 19}

Ⅱ. やりたい事、聞きたいこと

C#

1private List<IProduct> GetAmazonProducts() 2{ 3 var amazonProducts = new List<AmazonProduct>(); 4 amazonProducts.Add(new AmazonProduct() { Asin = "Asin001", Name = "Name001", Price = 100 }); 5 6 // エラー 7 // System.Collections.Generic.List<AmazonProduct>' を 8 // 'System.Collections.Generic.List<IProduct>' に暗黙的に変換できません 9 return amazonProducts; 10}
  1. 戻り値としてList<IProduct>を返したいと思っています。

しかし、上記のように書くとコンパイル時にエラーが出ます。
どのようにすればエラーが無くなりますでしょうか?

  1. 有効桁数が減ってしまいますが、long型の変数をint型にキャスト出来るのと同様に、

Amazon固有のAsinが無くなってしまいますが、
AmazonProductIProductにキャストできるものだと理解しています。
この理解がそもそも間違いなのでしょうか?
※下にサンプルコードを載せました

Ⅲ. 試したこと

2 のことが間違いないか確かめる為に以下のサンプルを書いた所動作しました。
よって、理解に間違いはないかと思いますが、
間違っていましたら指摘をお願い致します。

以下のように、List<IProduct>の変数productsを定義し、
products.AddRange()するとコンパイルが通りました。

C#

1// Amazonの商品をリストに追加 2var amazonProducts = new List<AmazonProduct>(); 3amazonProducts.Add(new AmazonProduct() { Asin = "Asin001", Name = "Name001", Price = 100 }); 4 5// eBayの商品をリストに追加 6var ebayProducts = new List<EbayProduct>(); 7ebayProducts.Add(new EbayProduct() { OwnerCountry = "JPN", Name = "Name002", Price = 200 }); 8 9var products = new List<IProduct>(); 10// 問題なくコンパイルが通る 11products.AddRange(amazonProducts); 12products.AddRange(ebayProducts);

Ⅳ. 補足情報(言語/FW/ツール等のバージョンなど)

.NET Core 1.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Panzer_vor

2017/02/01 03:23

ちなみにamazonProductsを宣言してる際に、あえて具象クラスのAmazonProductのリストとして定義しているのには意図がありますか。そこの宣言部をIProductにしたらコンパイルが通りそうな気がします。
aglkjggg

2017/02/01 03:25

ありがとうございます>< 凡ミスを真面目に考えていました・・・
Panzer_vor

2017/02/01 03:28

そんなこともありますね。ドンマイです><
guest

回答2

0

ベストアンサー

c#

1var amazonProducts = new List<AmazonProduct>();

のところを

c#

1var amazonProducts = new List<IProduct>();

に修正すればいいかと。

投稿2017/02/01 03:21

turbgraphics200

総合スコア4267

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aglkjggg

2017/02/01 03:25

ありがとうございます>< 凡ミスを真面目に考えていました・・・
guest

0

対処そのものはturbgraphics200さんがおっしゃるとおりと思います。

蛇足ですが、Listのインターフェース定義がList<out T>のように共変になっていればご質問のコードは期待通り動きます。実際はList<T>のように非変として定義されているので、ジェネリックの型引数が正確に一致していないと派生クラスとみなされません。

AmazonProductはIProductの派生なのだけど、
List<AmazonProduct>はList<IProduct>の派生とはみなさない・・・

Listは値を取り出すだけではなく値を入れる(Addメソッドの引数に要素が指定できる)ために非変として定義されているのでしょう。

これがIEnumerableなどだとIEnumerable<out T>つまり共変となっているので

AmazonProductがIProductの派生ならば
IEnumerable<AmazonProduct>もIEnumerable<IProduct>の派生と見做す・・・

ということになりますね。メソッドの戻り値がListではなくIEnumerableならば(つまり要素を取り出すことしかしないなら)IEnumerable<AmazonProduct>あるいはList<AmazonProduct>をそのままIEnumerable<IProduct>として返せます。

凡ミスとおっしゃっていますが真面目に考えてみてもいい話だとは思います。

投稿2017/02/01 04:02

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aglkjggg

2017/02/01 14:26 編集

ご回答ありがとうございます。 今までC#を書いていて深い理解がないまま書いてきた部分がありました。 今回の質問はその部分が露骨になってしまったようです。 KSwordOfHaste様の回答を見て、 「共変」、「反変」という単語が初めて見るものだった事もあり、 さっぱり意味が解らなかったので勉強し直しました>< ・共変性(Covariance)  最初に指定した型よりも強い派生型を使用できるようにする。  要素の追加、変更が出来ないことを保証する。  型の不正な書き換えが起こらないようにする仕組み、概念。  C# では out が共変を表す変性注釈。 ・反変性(Contravariance)  最初に指定した型の基底または弱い派生型を使用できるようにする。  型の不正な書き換えが起こないようにする仕組み、概念。  C# では in が反変を表す変性注釈。 ・C#のジェネリクスについての共変性、反変性  .NET Framework 4.0(C# 4.0)で可能になった  IEnumerable<T> は IEnumerable<out T> に変わった。 ・IEnumerable<out T>  IEnumerable<out T> は共変(Covariant)の典型的な例。  以下のような書き方が可能。  IEnumerable<string> strings = new List<string>();  IEnumerable<object> objects = strings;  共変(Covariant)でなければ上記のような書き方ができない。  例えば、IList<T> は共変(Covariant)ではないのでコンパイル時にエラーになる。 以上のことを踏まえて回答を読んだ所理解できました。 今回の質問の件、 Amazon固有の Asin で比較する処理が必要になった為、 List<IProduct>の定義では比較できないので、 List<AmazonProduct>で定義して、戻り値はIEnumerable<IProduct>にするという、 KSwordOfHaste様の回答が最もあった形でした。 長くなりましたが、 今回の件、深い理解を得る良い機会となりました。 簡単な解決策のみではなく、 理解するためのアドバイス、最適解となる手引をしていただきありがとうございました。 ※参考文献 http://ufcpp.net/study/csharp/sp4_variance.html http://msdn.microsoft.com/ja-jp/library/ee207183.aspx http://msdn.microsoft.com/ja-jp/library/dd233059.aspx
KSwordOfHaste

2017/02/01 15:08

aglkjgggさんのコードを最初に見たとき「やりたいことはすごくわかる」と思いコメントしてみました。 共変・反変を扱う機能は他の言語にもあるのでこの概念を活用する機会は少なくないと思います。かくいう自分は共変・反変のジェネリクス型を扱う際にはいつも混乱しがちで手が止まることが多いです... orz
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問