C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[7]: ジェネリックメソッドの制約列挙について


(過去ログ 169 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■97528 / inTopicNo.1)  ジェネリックメソッドの制約列挙について
  
□投稿者/ ちーず (5回)-(2021/06/02(Wed) 17:16:21)

分類:[C#] 

現在VisualStudioでアプリケーション開発を行っています。
イベントの共通処理をメソッドにして、メソッドの引数としてモデルクラスを格納するListを渡しています。
モデルクラスは3種類あって、
List<ModelClass1>, List<ModelClass2>, List<ModelClass3>といったようなリストを引数で渡したいと考えています。

現在ジェネリックを利用したメソッドを作ろうとしているのですが、where以降の制約の部分でクラスを列挙するとエラーが出てしまいます。
クラス型を列挙する方法などありましたら教えて頂きたいです。

// 作りたいメソッド例
public void Method<T> (T list) where List<ModelClass1>, Kist<ModelClass2>, List<ModelClass3>
{
共通の処理
}


引用返信 編集キー/
■97529 / inTopicNo.2)  Re[1]: ジェネリックメソッドの制約列挙について
□投稿者/ Hongliang (1179回)-(2021/06/02(Wed) 17:56:48)
C#では、型制約は型引数にしか適用できません。
なので
void Method<T>(...) where T : hogehoge
のように、Tをどう制約するかという話になります。

とりあえずは
void Method<T>(List<T> list)
のようにすれば任意のList<T>を渡せるようになります。

C#の型制約では和集合で制約を掛けることはできません。
Tに「ModelClass1またはModelClass2」というのは不可能です。
void Method<T>(List<T> list) where T : ModelClass1
のように、ModelClass1およびその派生クラスに制約することはできるのですが。

共通の処理としたい場合、ModelClass1〜ModelClass3に何らかのinterfaceを実装し、
それで制約することになります。
interface IModel {
    string Name { get; }
}
class ModelClass1 : IModel {
    public string Name { get; set; }
}
void Method<T>(List<T> list) where T : IModel {
    foreach (var item in list) {
        Trace.WriteLine(item.Name);
    }
}

引用返信 編集キー/
■97534 / inTopicNo.3)  Re[2]: ジェネリックメソッドの制約列挙について
□投稿者/ ら (4回)-(2021/06/03(Thu) 11:54:41)
No97529 (Hongliang さん) に返信

Interfaceを実装する形でやることにしたのですが、共通処理の部分でエラーが出てしまいます。
CSVファイルから一行ずつ抜き出したデータを、Modelクラスのプロパティにセットして、そのModelクラスごとListに格納していく処理なのですが、
「ModelからTに変換することができません」といったエラーが出ます。何か間違っている点や、解決策などありましたら教えて頂きたいです。

private void MakeList<T>(string s, List<T> dataList) where T : IModel
{
using (StreamReader File = new StreamReader(path, Encoding.GetEncoding("UTF-8")))
{
while (!File.EndOfStream)
{
string line = File.ReadLine();
list = line.Split(',');

// この処理以降にエラーが出ます

dataList.Add(new Model()
{
Name1 = _valueList[0],
Name2 = _valueList[1],
Name3 = _valueList[2],

             ・
             ・    
             ・
});
}
引用返信 編集キー/
■97535 / inTopicNo.4)  Re[3]: ジェネリックメソッドの制約列挙について
□投稿者/ 魔界の仮面弁士 (3115回)-(2021/06/03(Thu) 12:03:00)
2021/06/03(Thu) 12:30:53 編集(投稿者)

No97528 (ちーず さん) に返信
No97534 (ら さん) に返信

元質問者の方ですか? それとも別人?

[利用方法/規約]
http://bbs.wankuma.com/index.cgi?mode=man
>> 一貫して同じハンドルを使用し、場を混乱させないようにしましょう。
>> 同じハンドルの方が質問の背景がわかりやすいです


> Interfaceを実装する形でやることにしたのですが、共通処理の部分でエラーが出てしまいます。
> 「ModelからTに変換することができません」といったエラーが出ます。
ModelClass1〜3 に、IModel インターフェイスを実装してありますか?

// IModel インターフェイスを implements する
public class ModelClass1 : IModel { }
public class ModelClass2 : IModel { }
public class ModelClass3 : IModel { }


> // この処理以降にエラーが出ます
> dataList.Add(new Model()
Model クラスに、IModel インターフェイスを実装してありますか?
public class Model : IModel { }

もしも Model クラスが sealed されていないのであれば、
 where T : Model
にしておいて、
 // Model クラスから inherits する
 public class ModelClass1 : Model { }
 public class ModelClass2 : Model { }
 public class ModelClass3 : Model { }
という手もありそうですが、今回は List<T> なので
共変性/反変性の問題があるのか…。
引用返信 編集キー/
■97536 / inTopicNo.5)  Re[4]: ジェネリックメソッドの制約列挙について
□投稿者/ ちーず (6回)-(2021/06/03(Thu) 12:15:24)
No97535 (魔界の仮面弁士 さん) に返信
すみません、名前が間違って入っていました。元質問者です。
質問初心者のため、慣れていない部分があり申し訳ありません。

各モデルクラスではInterfaceを実装してあります。
Interfaceやモデルクラスの作り方がおかしいのかもしれません。
こんな感じで作っています。

// Interface
interface IModel
{
string Name { get; set; }
}

// Model1
public class Model1 : IModel
{
public string Name { get; set; }

public string Hoge { get; set; }

}

// Model2
public class Model2 : IModel
{
public string Name { get; set; }

public string HogeHoge { get; set; }

public string HogeHogeHoge { get; set; }
}

// Model3
public class Model2 : IModel
{
public string Name { get; set; }

public string HogeHoge { get; set; }

public string HogeHogeHoge { get; set; }

public string HogeHogeHogeHoge { get; set; }
}

何か間違いなどありましたらお教えいただきたいです。
引用返信 編集キー/
■97538 / inTopicNo.6)  Re[5]: ジェネリックメソッドの制約列挙について
□投稿者/ 魔界の仮面弁士 (3117回)-(2021/06/03(Thu) 13:07:16)
No97536 (ちーず さん) に返信
> 各モデルクラスではInterfaceを実装してあります。

No97528 では「ModelClass1」「ModelClass2」「ModelClass3」でした。
No97534 では「Model」でした。
No97536 では「Model1」「Model2」「Model3」です。

メソッド定義も微妙に変化していましたし、
実際にはどのようになっているのでしょうか。


> こんな感じで作っています。

private void MakeList<T>(string s, List<IModel> dataList) where T : IModel
private void MakeList<T>(string s, List<T> dataList) where T : IModel

この 2 つの違いはわかりますか?


たとえば、
 var a = new List<IModel>();
 var b = new List<Model1>();
があったとします。

この場合「b = a;」とすることはできませんよね。
「List<Model1> c = new List<IModel>();」が NG なのは当然です。

Control c = this.textBox1; とは書けても、
TextBox t = this.Controls[0]; にできないのと同じ理由。


そして、「a = b;」とも書けません。これは、
「List<IModel> c = new List<Model1>();」が NG だからです。

一見すると、c は IModel であるために
 c.Add(new Model2());
と書けそうに思えますが、実際のインスタンスは
「Model1 を継承したクラス」専用であり、
IModel や Model2 を受け入れるわけではないためです。



private void MakeList<T>(string s, List<IModel> dataList) where T : IModel
{
 dataList.Add(new Model1()); // OK
 dataList.Add(new Model()); // Model が IModel を実装していれば OK
}

private void MakeList<T>(string s, List<T> dataList) where T : IModel, new()
{
 dataList.Add(new T()); // OK
 dataList.Add(new Model()); // NG。T が Model であるとは限らない
 dataList.Add(new Model1()); // NG。T が Model1 とは限らない
}
引用返信 編集キー/
■97539 / inTopicNo.7)  Re[6]: ジェネリックメソッドの制約列挙について
□投稿者/ ちーず (7回)-(2021/06/03(Thu) 14:16:49)
No97538 (魔界の仮面弁士 さん) に返信

> No97528 では「ModelClass1」「ModelClass2」「ModelClass3」でした。
> No97534 では「Model」でした。
> No97536 では「Model1」「Model2」「Model3」です。
すみません。下はClassが抜けているだけで同じものを表しています。
また、ご丁寧に説明していただいて、なぜエラーが出ていたのか少し理解することができました。

> private void MakeList<T>(string s, List<IModel> dataList) where T : IModel
> {
>  dataList.Add(new Model1()); // OK
>  dataList.Add(new Model()); // Model が IModel を実装していれば OK
> }

この形で書いてみたのですが、次は呼び出し元でエラーが出てしまいます。

// 呼び出し元
MakeList(string, List<T>)

エラー:メソッドMakeList<T>(string s, List<IModel> dataList)の型引数は、使用法から推論できません。型引数を明示的に指定してください。
引用返信 編集キー/
■97541 / inTopicNo.8)  Re[7]: ジェネリックメソッドの制約列挙について
□投稿者/ 魔界の仮面弁士 (3118回)-(2021/06/03(Thu) 14:49:49)
2021/06/03(Thu) 16:52:22 編集(投稿者)

No97539 (ちーず さん) に返信
>>private void MakeList<T>(string s, List<IModel> dataList) where T : IModel
> この形で書いてみたのですが、次は呼び出し元でエラーが出てしまいます。

引数に T 型がないので型推論は効きません。
型引数を明示する必要があります。

var a = new List<Model1>();
MakeList<Model1>("Example", a);
var b = new List<IModel>();
MakeList<IModel>("Example", b);


または
 var c = new Hoge<Model1>();
 c.MakeList("Example", list);
のように、オブジェクトの上位で型引数を決めるという手もあります。


型推論を利かせたいなら
 private void MakeList<T>(string s, List<T> dataList) where T : IModel, new()
 {
  dataList.Add(new T { Name = s });
 }
のようにします。これなら型引数無しで
 var a = new List<Model1>();
 MakeList("Example", a);
と書けます。ただし
 var b = new List<IModel>();
 MakeList("Example", b);
はできません。


ところで、そもそも今回の質問範囲を見る限りでは
ジェネリック メソッドにする意図が見えません。

dataList を列挙している様子も無いですし、
結果的に T も使われていないようです。
内部で『dataList.Add(new Model()』することが目的なら
 private void MakeList(string s, List<Model> dataList)
 private void MakeList(string s, List<IModel> dataList)
などで良いのでは無いでしょうか。

あるいはファイルを IModel へとパースする目的であれば、
List<> は呼び出し側に用意させるのではなく、
 // private List<T> MakeList<T>(string s) where T : IModel
 private IEnumerable<T> MakeList<T>(string s) where T : IModel
のように、戻り値として return させた方が使いやすい気がします。
これなら yield も使えますし。
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -