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

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

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

Re[2]: ジェネリックメソッドでプリミティブ型以外はエラーにしたい


(過去ログ 124 を表示中)

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

■73990 / inTopicNo.1)  ジェネリックメソッドでプリミティブ型以外はエラーにしたい
  
□投稿者/ キム (24回)-(2014/11/20(Thu) 15:44:14)

分類:[C#] 

Visual C# 2010 / .NET Framework 4.0 CP / クラスライブラリプロジェクト


C#ジェネリックメソッドを作成していますが、型引数Tがプリミティブ型以外の場合にエラーにしたいです。
それも実行時エラーではなく、コンパイル時エラーとしたいのです。

    private static T foo<T>(T arg) where T : struct
    {
        ...
    }

↑のようにすると参照型はエラーに出来ますが、構造体はエラーに出来ません。

    private static T foo<T>(T arg)
    {
        if (!typeof(T).IsPrimitive)
        {
            throw new ArgumentException();
        }
        ...
    }

↑のようにするとプリミティブ型以外はエラーに出来ますが、実行時エラーです。

プリミティブ型以外をコンパイル時エラーとする方法はありますでしょうか?
お知恵をお貸しいただけると嬉しいです。

引用返信 編集キー/
■73992 / inTopicNo.2)  Re[1]: ジェネリックメソッドでプリミティブ型以外はエラーにしたい
□投稿者/ PANG2 (47回)-(2014/11/20(Thu) 17:17:51)
IsPrimitiveな型は14個しかないので、すべてのメソッドを定義する

public class myClass
{
	public static Boolean foo(Boolean arg) { return foo<Boolean>(arg); }
	public static Byte foo(Byte arg) { return foo<Byte>(arg); }
	public static SByte foo(SByte arg) { return foo<SByte>(arg); }
	public static Int16 foo(Int16 arg) { return foo<Int16>(arg); }
	public static UInt16 foo(UInt16 arg) { return foo<UInt16>(arg); }
	public static Int32 foo(Int32 arg) { return foo<Int32>(arg); }
	public static UInt32 foo(UInt32 arg) { return foo<UInt32>(arg); }
	public static Int64 foo(Int64 arg) { return foo<Int64>(arg); }
	public static UInt64 foo(UInt64 arg) { return foo<UInt64>(arg); }
	public static IntPtr foo(IntPtr arg) { return foo<IntPtr>(arg); }
	public static UIntPtr foo(UIntPtr arg) { return foo<UIntPtr>(arg); }
	public static Char foo(Char arg) { return foo<Char>(arg); }
	public static Double foo(Double arg) { return foo<Double>(arg); }
	public static Single foo(Single arg) { return foo<Single>(arg); }

	private static T foo<T>(T arg)
	{
		//実装
		return arg;
	}
} 

引用返信 編集キー/
■73997 / inTopicNo.3)  Re[1]: ジェネリックメソッドでプリミティブ型以外はエラーにしたい
□投稿者/ 魔界の仮面弁士 (176回)-(2014/11/20(Thu) 19:44:30)
# 回答にあらず

No73990 (キム さん) に返信
> C#ジェネリックメソッドを作成していますが、型引数Tがプリミティブ型以外の場合にエラーにしたいです。

そういえば、C# にとってのプリミティブ型って何でしたっけ…。
https://social.msdn.microsoft.com/Search/ja-jp/?Query=%E3%83%97%E3%83%AA%E3%83%9F%E3%83%86%E3%82%A3%E3%83%96%E5%9E%8B


VB では、DateTime 型や String 型もプリミティブ型と呼ばれますが、
これらは、CLR にとっては IsPrimitive が False ですね。
http://msdn.microsoft.com/en-us/library/aa711900%28v=vs.71%29.aspx
http://msdn.microsoft.com/ja-jp/library/aa711900%28v=vs.71%29.aspx
引用返信 編集キー/
■73998 / inTopicNo.4)  Re[2]: ジェネリックメソッドでプリミティブ型以外はエラーにしたい
□投稿者/ キム (25回)-(2014/11/20(Thu) 19:48:16)
No73992 (PANG2 さん) に返信

お返事ありがとうございます。

早速試したところ、うまくいきました。
ただ、privateなジェネリック実装メソッドをpublicメソッドと同じ名前のままにすると、
コンパイルエラーにはなるけど保護レベルエラーとなって分かりにくいです。
(コンパイラがprivateなジェネリック実装メソッドを呼ぼうとするので)

そこで、privateなジェネリック実装メソッドは名前を変えることにしました。

    public static Boolean foo(Boolean arg) { return fooImpl<Boolean>(arg); }
    public static Byte foo(Byte arg) { return fooImpl<Byte>(arg); }
       .
       .
       .
    public static Byte foo(Single arg) { return fooImpl<Single>(arg); }

    private static T fooImpl<T>(T arg)
    {
        //実装...
    }

おかげさまで解決できて嬉しいです。
これで解決しましたが、いくつか似たようなパターンがあるので、そちらを試してから解決済みとしたいと思います。

引用返信 編集キー/
■74000 / inTopicNo.5)  Re[2]: ジェネリックメソッドでプリミティブ型以外はエラーにしたい
□投稿者/ キム (26回)-(2014/11/20(Thu) 20:38:10)
No73997 (魔界の仮面弁士 さん) に返信

読んでくださってありがとうございます。

> そういえば、C# にとってのプリミティブ型って何でしたっけ…。

私も調査時にリンクのページを見て不思議な感じがしました。
VBのプリミティブ型はちょっと意外でしたし、『C# プリミティブ型』での検索ではぴったりなものがヒットしないし。

私は、C#言語仕様 Version 4.0(CSharp Language Specification.doc)でいうところの単純型と同義と思っていました。
が、ちょっと違うようですね。
言語仕様では『単純型』は明確に定義されていますが、『プリミティブ』という表記はほとんど無くて、あっても
『intやdoubleなどのプリミティブ型...』のように明確な定義がないです。

sizeof (C# Reference)
http://msdn.microsoft.com/ja-jp/library/eahchzkf.aspx

を見ると built-in types (組み込み型) と表記されていますが、一覧は単純型そのものなので、
組み込み型 = 単純型 はいえると思いますが、『C#にとってのプリミティブ型』には明確な定義がないのかなあ。

Type.IsPrimitive
http://msdn.microsoft.com/ja-jp/library/system.type.isprimitive(v=vs.100).aspx

ここには明確な一覧がありますし、.NET Framework クラスライブラリ でのプリミティブというのは明確ですね。


今回は内部で使用する.NET Framework クラスライブラリ のメソッドが『プリミティブ型じゃないと例外を投げる』
と書いてあるので、Type.IsPrimitiveがtrueを返すものを対象としたいです。

私の表現もあいまいでしたね^^;
引用返信 編集キー/
■74002 / inTopicNo.6)  Re[3]: ジェネリックメソッドでプリミティブ型以外はエラーにしたい
□投稿者/ キム (27回)-(2014/11/20(Thu) 21:03:22)
No73998 (キム さん) に返信

すみません、一点問題がありました。
今回は3つのジェネリックメソッドを作るのですが、そのうちの1つだけが他とは違って、

    private static T bar<T>(int arg)
    {
        T result;
        // 実装...
        return result;
    }

のいうように引数にT型を含んでいません。
なので、これをオーバーロードで解決しようとすると戻り値だけが異なるオーバーロードになってしまいます。
戻り値型のみが異なるオーバーロードは(少なくともC#レベルでは)出来ないので、教えていただいた方法では
このパターンを解決できませんでした。

自分では、いまのところ『IntPtrとUIntPtrはサポート外として、インターフェース制約を厳しくすることで
<なるべく> コンパイルエラーになるようにする』ぐらいしか思いつかないです。

    public static T bar<T>(int arg) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
    {
        // コンパイルエラーに出来なかったものは実行時エラーでごめんなさい;;;;
        if (!typeof(T).IsPrimitive)
        {
            throw new ArgumentException();
        }
        
        T result;
        // 実装...
        return result;
    }

同じインターフェースを実装している構造体には無力ですし、『極力組み込み型と同じ』扱いとなるようにこれらの
インターフェースを実装している構造体は沢山ありそうなので余り意味ないですね。

何かこのパターンでも有効な方法はありますか?

引用返信 編集キー/
■74177 / inTopicNo.7)  Re[4]: ジェネリックメソッドでプリミティブ型以外はエラーにしたい
□投稿者/ キム (28回)-(2014/12/05(Fri) 15:07:34)
2014/12/05(Fri) 16:49:49 編集(投稿者)

No74002 (キム さん) に返信

遅くなってごめんなさい。
結局、今回は『IntPtrとUIntPtrはサポート外として、インターフェース制約を厳しくすることで<なるべく> コンパイルエラーになるようにする』
方法を採用しました。

が、用途によっては無理にジェネリックスとオーバーロードにこだわらず、別名のメソッドを対応する型の分だけ用意する方法もありかなと思います。

最後に、ジェネリックメソッドで特定の型以外をコンパイルエラートする方法をまとめて、解決とさせていただきます。。
・引数にT型を含むものは No73990 でPANG2さんが示した下さった方法で解決できる。
・引数にT型を含まないものは制約である程度実現する。
 ただし、今回のように制約として用意されていない条件(プリミティブ型以外)は完全には実現できないのである程度であきらめる。
 または、オーバーロードにこだわらず、別名のメソッドを対応する型の分だけ用意する。

その他
『C#でのプリミティブ型って何?』でいろいろ調べて勉強になりました。

ごめんなさい、解決済みチェックを付け忘れていました。
解決済み
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -