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

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

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

Re[8]: IList の項目の Type を取得する方法


(過去ログ 25 を表示中)

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

■11428 / inTopicNo.1)  IList の項目の Type を取得する方法
  
□投稿者/ 囚人 (255回)-(2007/12/13(Thu) 16:40:11)

分類:[.NET 全般] 

お世話になります。

IList の項目の Type から、リフレクションでインスタンスを得たいと考えています。

IList l = // どこからか IList のインスタンスを得る。
object o = Activator.CreateInstace(typeof(l[0]));

しかし、IList が 0 件の場合は上記では動作しません(IList が null の場合は考えないとします)。もちろん、0 件とは言え、IList のインスタンスの型は存在し、そのアイテムの型も存在します。

IList が 0 件の場合でも、項目の Type を得る手段はあるでしょうか?

因みに、ジェネリクスは使えない環境です(つまり、.NET 1.1 です)。

引用返信 編集キー/
■11429 / inTopicNo.2)  Re[1]: IList の項目の Type を取得する方法
□投稿者/ 魔界の仮面弁士 (547回)-(2007/12/13(Thu) 17:00:26)
2007/12/13(Thu) 17:11:35 編集(投稿者)

No11428 (囚人 さん) に返信
> IList l = // どこからか IList のインスタンスを得る。
これはたとえば、
 class Foo :IList
 {
  //全メンバ実装を throw new NotImplementedException();
 }
のインスタンスでも OK ということですか?

> object o = Activator.CreateInstace(typeof(l[0]));
> しかし、IList が 0 件の場合は上記では動作しません(IList が null の場合は考えないとします)。
インスタンスから得るのであれば、typeof ではなく、
 object o = Activator.CreateInstance(l.GetType());
のようになる気がします。

個々のメンバの型という意味だと、IList のインデクサは
 object IList.this[int index] { get; set; }
なので、IList から見れば object にしかならない気も。
引用返信 編集キー/
■11430 / inTopicNo.3)  Re[1]: IList の項目の Type を取得する方法
□投稿者/ れい (303回)-(2007/12/13(Thu) 17:16:10)
No11428 (囚人 さん) に返信
> しかし、IList が 0 件の場合は上記では動作しません(IList が null の場合は考えないとします)。もちろん、0 件とは言え、IList のインスタンスの型は存在し、そのアイテムの型も存在します。
>
> IList が 0 件の場合でも、項目の Type を得る手段はあるでしょうか?

StringCollectionなどだと型は「String」固定ですが、
ArrayListなんかだと全てのオブジェクトを入れることができます。
この場合はObjectのインスタンスを作りたいのですか?

たとえばControls.ItemCollectionだとどういった振る舞いを期待してますか?
Controlの新しいインスタンスを作るのでしょうか?

引用返信 編集キー/
■11432 / inTopicNo.4)  Re[2]: IList の項目の Type を取得する方法
□投稿者/ 囚人 (256回)-(2007/12/13(Thu) 17:21:49)
ありがとうございます。


> class Foo :IList
> {
>  //全メンバ実装を throw new NotImplementedException();
> }
>のインスタンスでも OK ということですか?

はい。OK です。ただし、今欲しいのは、Foo コレクションの項目の Type です。項目と言ったら分かりにくかったですね。各要素の事です。

ですので、
object o = Activator.CreateInstance(l.GetType());
ではなく、
object o = Activator.CreateInstance(l[0].GetType());
が欲しい形です。



>個々のメンバの型という意味だと、IList のインデクサは
> object IList.this[int index] { get; set; }
>なので、IList から見れば object にしかならない気も。

もし要素が1つでもあれば、インデクサから type を得る事ができるので望みの事ができるのですが、0 個の場合インデクサが使えないのでどうしたものかと。


…とここまで考えて、よく考えてみれば各要素が「全て同じ型」とは限らないから、そんな手段あるわけないですよね…。
引用返信 編集キー/
■11433 / inTopicNo.5)  Re[3]: IList の項目の Type を取得する方法
□投稿者/ 囚人 (257回)-(2007/12/13(Thu) 17:24:17)
ありがとうございます。


>StringCollectionなどだと型は「String」固定ですが、
>ArrayListなんかだと全てのオブジェクトを入れることができます。
>この場合はObjectのインスタンスを作りたいのですか?


その考慮が抜けていました。
バラバラの要素を入れる事があまりないので、完全に抜けてました。
て事は無理ですよね…。


ありがとうございます。
解決済み
引用返信 編集キー/
■11434 / inTopicNo.6)  Re[4]: IList の項目の Type を取得する方法
□投稿者/ 囚人 (258回)-(2007/12/13(Thu) 17:32:56)
「解決」にしましたが、更に疑問を。
本題から外れてジェネリクスも考慮に入れます。

「シールドクラス」を要素に持つことが分かっているコレクション(例えば T が シールドのジェネリクスコレクションを使う、また StringCollection など)なら「要素の型」を得る事ができるのではないか、或いはできてもおかしくないと思います。

何か方法を知っていたら教えて下さい。
引用返信 編集キー/
■11436 / inTopicNo.7)  Re[5]: IList の項目の Type を取得する方法
□投稿者/ れい (305回)-(2007/12/13(Thu) 17:51:54)
2007/12/13(Thu) 17:52:22 編集(投稿者)

No11434 (囚人 さん) に返信
> 「シールドクラス」を要素に持つことが分かっているコレクション(例えば T が シールドのジェネリクスコレクションを使う、また StringCollection など)なら「要素の型」を得る事ができるのではないか、或いはできてもおかしくないと思います。

???
StringCollectionだとわかってるならnew String()でいいですよね?
Tが具体的にわかってるなら、new T()でいいですよね?

もし、Tが「なんだかわからないけどシールドである」というような制約
(たとえば「IList<T> where T: sealed」こんな感じ?)
があるならできてもおかしくないと思いますが。

interface ITypedEnumerator : IEnumerator {
Type GetItemType();
}
こんなのがあっても良いとは思います。

引用返信 編集キー/
■11439 / inTopicNo.8)  Re[6]: IList の項目の Type を取得する方法
□投稿者/ 囚人 (259回)-(2007/12/13(Thu) 17:56:53)
>もし、Tが「なんだかわからないけどシールドである」というような制約
>(たとえば「IList<T> where T: sealed」こんな感じ?)
>があるならできてもおかしくないと思いますが。

はい。
コレクションの型が分からない、コレクションの要素の型も分からない、コレクションの要素がシールドであるという前提
という条件で、コレクションの要素をインスタンス化できてもいいんじゃないかと。
擬似コードで書くと

IList list = // どっかから IList を取得

if(list.リストのアイテムの型 is シールド)
{
object o Activator.CreateInstace(list.リストのアイテムの型);
}

みたいな。
引用返信 編集キー/
■11440 / inTopicNo.9)  Re[7]: IList の項目の Type を取得する方法
□投稿者/ れい (306回)-(2007/12/13(Thu) 18:05:49)
No11439 (囚人 さん) に返信
> はい。
> コレクションの型が分からない、コレクションの要素の型も分からない、コレクションの要素がシールドであるという前提
> という条件で、コレクションの要素をインスタンス化できてもいいんじゃないかと。

んー。なんか本末転倒気味なきがします。

> IList list = // どっかから IList を取得
> if(list.リストのアイテムの型 is シールド)
> {
> object o Activator.CreateInstace(list.リストのアイテムの型);
> }

「list.リストのアイテムの型」という関数があるなら、
「シールドでないとインスタンス化できない」という制限を課す必要もないと思います。

もっと直接「IListに既定の型を取得するメソッドがほしい」というほうがいいような。

で、それならジェネリクスの方がいいと思います。
ないなら独自インタフェースかな?
引用返信 編集キー/
■11441 / inTopicNo.10)  Re[8]: IList の項目の Type を取得する方法
□投稿者/ 囚人 (260回)-(2007/12/13(Thu) 18:12:32)
ん〜、確かに独自ジェネリクスインターフェースで
where T : new()
とかにした方がいいですね。

引用返信 編集キー/
■11442 / inTopicNo.11)  Re[9]: IList の項目の Type を取得する方法
□投稿者/ 囚人 (261回)-(2007/12/13(Thu) 18:22:11)
ありがとうございました。とりあえず解決します。
解決済み
引用返信 編集キー/
■11443 / inTopicNo.12)  Re[5]: IList の項目の Type を取得する方法
□投稿者/ 魔界の仮面弁士 (548回)-(2007/12/13(Thu) 18:23:43)
No11434 (囚人 さん) に返信
> 「シールドクラス」
MS 的には「シール クラス」らしいです。


> また StringCollection など)なら「要素の型」を得る事ができるのではないか、
目的が見えないので何とも言えませんが、とりあえず、
公開インデクサの戻り値の型を調べてみては如何でしょうか。

1. DefaultMemberAttribute を得る
2. その Name を持った PropertyInfo を得る
3. そこから PropertyType を得る


ただし、下記のような出鱈目な実装に対しては、byte型を返す事になりますけど…。


class Hoge : IList
{
  public int Count { get { return 1; } }

  // foreach で列挙されたら String 型
  public IEnumerator GetEnumerator() { yield return "String"; }

  // インデクサでは Byte 型
  object IList.this[int index] { get { return null; } set { } }
  public byte this[int index] { get { return 0xff; } }

  // 登録時には DateTime 型
  public int Add(DateTime value) { return 0; }
  int IList.Add(object value) { return 0; }

  // 以下略
}

引用返信 編集キー/
■11445 / inTopicNo.13)  Re[10]: IList の項目の Type を取得する方法
□投稿者/ 囚人 (262回)-(2007/12/13(Thu) 18:37:17)
>1. DefaultMemberAttribute を得る
>2. その Name を持った PropertyInfo を得る
>3. そこから PropertyType を得る


おお!!
「DefaultMemberAttribute を得る」は思いつきませんでした。
IList のオブジェクトの型をゲットして、それから DefaultMemberAttribute をゲットするのは良さそうです。
ありがとうございます。
引用返信 編集キー/
■11446 / inTopicNo.14)  Re[7]: IList の項目の Type を取得する方法
□投稿者/ NyaRuRu (28回)-(2007/12/13(Thu) 18:54:49)
2007/12/13(Thu) 18:57:49 編集(投稿者)
2007/12/13(Thu) 18:56:40 編集(投稿者)
2007/12/13(Thu) 18:55:43 編集(投稿者)

■No11439 (囚人 さん) に返信
> コレクションの型が分からない、コレクションの要素の型も分からない、コレクションの要素がシールドであるという前提
> という条件で、コレクションの要素をインスタンス化できてもいいんじゃないかと。

ご質問の内容を恐らくほとんど理解できていないのですが,
あるインスタンス o が IEnumerable<T> を実装しているとき,
その T を得るのは以下のコードで可能です.

public static Type GetCollectionTypeFromInstance(object o)
{
    Type instType = o.GetType();

    var enumrableTypes = from i in instType.GetInterfaces()
                         where i.IsGenericType
                         where i.GetGenericTypeDefinition() == typeof(IEnumerable<>)
                         select i.GetGenericArguments()[0];

    return enumrableTypes.Single();
}

が,この方法には問題もあります.
1) GetInterfaces はその型が実装する全てのインターフェイスを返すわけではない
   (暗黙に実装されるインターフェイスが存在する)
2) あるインスタンス o は,IEnumerable<T1> と IEnumerable<T2> というように
   複数の IEnumerable<> を実装しているかもしれない

例)

object array = Enum.GetValues(typeof(DayOfWeek)) as DayOfWeek[];
var interfaces = array.GetType().GetInterfaces();
var a = array is IEnumerable<int>; // true
var b = array is IEnumerable<DayOfWeek>; // true
var ia = interfaces.Contains(typeof(IEnumerable<int>)); //false
var ib = interfaces.Contains(typeof(IEnumerable<DayOfWeek>)); //true

DayOfWeek[] 型に対する GetInterfaces() は IEnumerable<int> を含みませんが,
実際には DayOfWeek[] 型のインスタンスは IEnumerable<int> を実装しています.

囚人さんはどうも汎用的な何かのコードを書こうとされているようですが,
何となくの印象としては,
条件が不足していて実装がひとつに定まらない問題になってしまっている気がします.

まあ上のコードが何かの参考になりましたら幸いです.

解決済み
引用返信 編集キー/
■11447 / inTopicNo.15)  Re[8]: IList の項目の Type を取得する方法
□投稿者/ 囚人 (263回)-(2007/12/13(Thu) 19:50:35)
はい。元々は汎用的なコントロールを作ろうとしていたのですが、本題は別の方法で臨む事にしました。従って、何か問題を解決しようとしてるのではなく、完全に好奇心です。

コレクションには「いろんなものが入るもの」と「一つの型しか入らないもの」で大別できると思います。DTO を入れておくだけの入れ物は大概後者です。

要素がシールだという前提を持てるなら、必ず「一つの型しか入らない」コレクションとなります。そういうコレクションの場合は、例え要素の数が 0 でも、要素の型を得る手段があってもいいはずだ、と考えました。


>まあ上のコードが何かの参考になりましたら幸いです.

ありがとうございます。参考になります。


いろいろ考えてみると、どうやら「一つの型しか入らないコレクション」がそもそも怪しい存在のようですね。たとえそうだったとしても、Add とインデクサの型が違う、IEnumerable<T> を複数実装できる、などがあるなら少なくとも判定は無理ですね。
解決済み
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -