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

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

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

Re[3]: ジェネリック型の名前の取得について


(過去ログ 114 を表示中)

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

■67538 / inTopicNo.1)  ジェネリック型の名前の取得について
  
□投稿者/ あいざっく (1回)-(2013/08/08(Thu) 14:22:48)

分類:[C#] 

初めて質問させていただきます。
ジェネリック型の名前の取得についてわからないことがありますので、ご存知の方が
おられましたら教えてください。

やりたいことは、与えられたジェネリック型について、できるだけわかりやすい名前を
取得することです。

例えば、以下のようなコードがあったとします。

public class Main
{
    public void Main()
    {
        hogehoge<string> hogehoge_obj = new hogehoge<string>();
        int result = hogehoge_obj.hoehoe(null);
    }
}

public class hogehoge<T>
{
    public int hoehoe(IEnumarable<T> p)
    {
        if (p == null)
        {
            Type t = typeof(IEnumarable<T>);
            throw (new ArgumentNullException(string.Format("'{0}'型のパラメタpがnullです。", t.Name)));
        }

        .........

    }

}

このコードでは、クラスhogehogeのメソッドhoehoeでパラメタpの値が不正な値
(例えばnull)だったときに例外を通知するのですが、その例外のメッセージに原因と
なったパラメタの型名を添えています。

このコードを実行すると、通知される例外には

"'IEnumarable`1'型のパラメタpがnullです。"

というメッセージがのるのですが、その代わりに

"'IEnumarable<string>'型のパラメタpがnullです。"

というメッセージにできないか、方法を探しています。

いろいろやってみて、以下のようにすれば期待しているメッセージがのることが確認
できたのですが、

throw (new ArgumentNullException(string.Format("'IEnumarable<{0}>'型のパラメタpがnullです。", typeof(T).Name)));

この方法だと例えばIDictionary<int, string>型の名前を取得する場合に別のコードを
書かないといけないので、もっと汎用的に使える方法がないか探しています。

いいやり方をご存知の方がおられましたら教えてください。
よろしくお願いします。

引用返信 編集キー/
■67539 / inTopicNo.2)  Re[1]: ジェネリック型の名前の取得について
□投稿者/ 禅 (1回)-(2013/08/08(Thu) 14:50:48)
pがnullであれば、Tが何なのかは知りようがないのではないですか?
それはさておき、何の目的でTの型をメッセージとして利用したいのでしょうか?
引用返信 編集キー/
■67540 / inTopicNo.3)  Re[1]: ジェネリック型の名前の取得について
□投稿者/ 魔界の仮面弁士 (298回)-(2013/08/08(Thu) 15:47:30)
No67538 (あいざっく さん) に返信
> public class Main
> {
> public void Main()
> {
クラス名と同じ名前のメソッドは作れません(VBでは可能)。
それに Main メソッドなら、通常は static ですよね。


> public int hoehoe(IEnumarable<T> p)
IEnumerable<T> ではなく?



> throw (new ArgumentNullException(string.Format("'{0}'型のパラメタpがnullです。", t.Name)));
型パラメータの情報も必要な場合は、GetGenericArguments も併用してみてください。


> 例えばIDictionary<int, string>型の名前を
「List<IDictionary<int, IEnumerable<int>>>」なんて事も。
引用返信 編集キー/
■67541 / inTopicNo.4)  Re[2]: ジェネリック型の名前の取得について
□投稿者/ あいざっく (2回)-(2013/08/08(Thu) 15:51:04)
No67539 (禅 さん) に返信
> pがnullであれば、Tが何なのかは知りようがないのではないですか?
> それはさておき、何の目的でTの型をメッセージとして利用したいのでしょうか?

早速のコメントありがとうございます。

まず、「pがnullであればTが何なのかは知りようがないのではないか」についてですが、説明のコードを簡単にするためにpがnullかどうかの比較だけをするコードにしていました。
※pがnullでなくても本質的に問題は変わらないと考えていたせいもあります。

実際に私が開発に使っているコードでは、以下のように引数pの型がobjectであり、pがnullでないことが判明した後にサポートされている型であるかどうかのチェックが入ります。


public int hoehoe(object p)
{
    if (p == null)
    {
        Type t = typeof(IEnumarable<T>);
        throw (new ArgumentNullException(string.Format("'{0}'型のパラメタpがnullです。", t.Name)));
    }
    if (!(p is IEnumerable<T>))
    {
        Type t = typeof(IEnumarable<T>);
        throw (new ArgumentException(string.Format("'{0}'型のパラメタpに'{1}'型のオブジェクトが与えられています。", t.Name, p.GetType().Name)));
    }

    .........

}


次に「何の目的でTの型をメッセージとして利用したいのか」についてですが、「その方が例外の元になった障害の原因を直観的に推測しやすいと考えたから」が答えとなります。





引用返信 編集キー/
■67542 / inTopicNo.5)  Re[2]: ジェネリック型の名前の取得について
□投稿者/ あいざっく (3回)-(2013/08/08(Thu) 16:16:20)
コメントありがとうございます。

■No67540 (魔界の仮面弁士 さん) に返信
> ■No67538 (あいざっく さん) に返信
>>public class Main
>>{
>>    public void Main()
>>    {
> クラス名と同じ名前のメソッドは作れません(VBでは可能)。
> それに Main メソッドなら、通常は static ですよね。

大変失礼しました orz
確かにその通りです。いい加減な説明コードで申し訳ありません。


>>    public int hoehoe(IEnumarable<T> p)
> IEnumerable<T> ではなく?

はい、ご指摘の通り説明コードの綴りミスです。
実際の開発コードでは IEnumerable になっております。


>>throw (new ArgumentNullException(string.Format("'{0}'型のパラメタpがnullです。", t.Name)));
> 型パラメータの情報も必要な場合は、GetGenericArguments も併用してみてください。
> 
> 
>>例えばIDictionary<int, string>型の名前を
> 「List<IDictionary<int, IEnumerable<int>>>」なんて事も。

教えていただいた GetGenericArguments を使用して型の名前を取得するメソッドを組んでみました。

private static string GetImplementedTypeName(Type type)
{
    if (!type.IsGenericType)
        return (type.Name);
    if (type.IsGenericTypeDefinition)
        return (type.Name);
    string parameter_names = string.Join(",", (from t_arg in type.GetGenericArguments() select t_arg.Name));
    return (string.Format("{0}<{1}>", type.Name, parameter_names));
}

このメソッドに typeof(IEnumerable<string>) を渡すと "IEnumerable`1<string>" が返り、"IEnumerable" でなく "IEnumerable`1" が返る部分以外は期待通りです。
"IEnumerable" という名前を取得する素直な方法がなさげなので、他にいい方法がなければこれで行こうかなと思います。

コメントしてくださった方々、ありがとうございました。

解決済み
引用返信 編集キー/
■67545 / inTopicNo.6)  Re[3]: ジェネリック型の名前の取得について
□投稿者/ 魔界の仮面弁士 (299回)-(2013/08/08(Thu) 17:14:51)
No67542 (あいざっく さん) に返信
> "IEnumerable" でなく "IEnumerable`1" が返る部分以外は期待通りです。

末尾にあるバックチック以降の数値は、型パラメータの個数を表しています。
http://msdn.microsoft.com/ja-jp/library/vstudio/w3f99sx1.aspx


不要なら文字列処理で切り出しても良いと思いますが、
 //using System.Collections;
 //using System.Collections.Generic;
 a = GetImplementedTypeName(typeof(IEnumerable)); //「IEnumerable」
 b = GetImplementedTypeName(typeof(IEnumerable<>)); //「IEnumerable`1」
 c = GetImplementedTypeName(typeof(IEnumerable<int>));//「IEnumerable`1<Int32>」
の場合に、a と b を区別できなくなります。

なのでもう一手間加えて、b を「IEnumerable<>」で返すようにした方が良いかも。



> 「その方が例外の元になった障害の原因を直観的に推測しやすいと考えたから」
であれば、名前空間も出力した方が良いかと。


> 他にいい方法がなければこれで行こうかなと思います。
再帰的に処理しないとまずいかも。今の実装だと、
 typeof(List<Dictionary<string, byte>>)
が『List`1<Dictionary`2>』になってしまいますよ。
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -