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

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

ただいまの
回答率

90.53%

  • C#

    8836questions

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

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

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,155

aglkjggg

score 732

 Ⅰ. 前提

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

  1. IProduct
    商品について必要最低限を定義したインターフェイス
  2. AmazonProduct
    Amazonの商品について定義したクラス(IProductを継承)
  3. EbayProduct
    Ebayの商品について定義したクラス(IProductを継承)
interface IProduct
{
  string Name { get; set; }
  int Price { get; set; }
}

public class AmazonProduct : IProduct
{
  public string Asin { get; set; } // Amazonグループだけが取り扱う特殊な番号
  public string Name { get; set; }
  public int Price { get; set; }
}

public class EbayProduct : IProduct
{
  public Country OwnerCountry { get; set; } // 出品者の国
  public string Name { get; set; }
  public int Price { get; set; }
}

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

private List<IProduct> GetAmazonProducts()
{
  var amazonProducts = new List<AmazonProduct>();
  amazonProducts.Add(new AmazonProduct() { Asin = "Asin001", Name = "Name001", Price = 100 });

  // エラー
  // System.Collections.Generic.List<AmazonProduct>' を 
  // 'System.Collections.Generic.List<IProduct>' に暗黙的に変換できません
  return amazonProducts;
}
  1. 戻り値としてList<IProduct>を返したいと思っています。
    しかし、上記のように書くとコンパイル時にエラーが出ます。
    どのようにすればエラーが無くなりますでしょうか?

  2. 有効桁数が減ってしまいますが、long型の変数をint型にキャスト出来るのと同様に、
    Amazon固有のAsinが無くなってしまいますが、
    AmazonProductIProductにキャストできるものだと理解しています。
    この理解がそもそも間違いなのでしょうか?
    ※下にサンプルコードを載せました

 Ⅲ. 試したこと

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

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

// Amazonの商品をリストに追加
var amazonProducts = new List<AmazonProduct>();
amazonProducts.Add(new AmazonProduct() { Asin = "Asin001", Name = "Name001", Price = 100 });

// eBayの商品をリストに追加
var ebayProducts = new List<EbayProduct>();
ebayProducts.Add(new EbayProduct() { OwnerCountry = "JPN", Name = "Name002", Price = 200 });

var products = new List<IProduct>();
// 問題なくコンパイルが通る
products.AddRange(amazonProducts);
products.AddRange(ebayProducts);

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

.NET Core 1.0

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Panzer_vor

    2017/02/01 12:23

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

    キャンセル

  • aglkjggg

    2017/02/01 12:25

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

    キャンセル

  • Panzer_vor

    2017/02/01 12:28

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

    キャンセル

回答 2

checkベストアンサー

+4

var amazonProducts = new List<AmazonProduct>();


のところを

var amazonProducts = new List<IProduct>();


に修正すればいいかと。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/01 12:25

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

    キャンセル

+1

対処そのものは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 23: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

    キャンセル

  • 2017/02/02 00:08

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

    キャンセル

同じタグがついた質問を見る

  • C#

    8836questions

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