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

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

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

Re[6]: enumの型指定について


(過去ログ 61 を表示中)

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

■35410 / inTopicNo.1)  enumの型指定について
  
□投稿者/ pirosuke (1回)-(2009/04/29(Wed) 01:40:36)

分類:[C#] 

はじめまして。
pirosukeと申します。

環境
WindowsXP
VS2005

enumの型指定時の動作について質問です。

まず、以下のようなenumを定義したとします。

public enum test{
	None = 0,
	All = ~None,
}

上記enumを型指定した場合、
byte型またはushort型に指定した場合のみがコンパイルエラーになります。

// OK
public enum test : uint{
	None = 0,
	All = ~None,
}

// NG
public enum test : byte{
	None = 0,
	All = ~None,
}

エラーの内容は「定数値 '-1' を 'byte' に変換できません。」です。

内容から~Noneがbyteとushortの場合-1になっているのはわかります。

ですが、なぜビット数に違いはあれど同じ符号なし整数のuintとulongはOKで、
byteとushortは~Noneが-1になりNGとなってしまうのでしょうか?
uintとulongもエラーならまだ納得できましたが…

単なる仕様といわれればそれまでですが^^;

色々と検索してみても、探し方が悪いのか同様の疑問を持った方すら発見できませんでした。
そもそもこの点に疑問を持つことがおかしいのかとすら思い始めてきたところです。

また、回避策としては、0xFF等をAllに設定してあげればよいというのは理解してます。
その上での単純な知的好奇心からの質問です。

以上、よろしくお願いいたします。

引用返信 編集キー/
■35411 / inTopicNo.2)  Re[1]: enumの型指定について
□投稿者/ よねKEN (324回)-(2009/04/29(Wed) 02:39:15)
2009/04/29(Wed) 02:39:36 編集(投稿者)

言語仕様書の列挙体の箇所では特に言及されていないようなので(見落としているかもしれませんが)、
通常の定数値やその演算結果の扱いと同じと考えられます。


int i = 0; // ← intの0
byte b = 0; // ← intの0だけど、左辺がbyte型のため暗黙的に0をbyte型とみなす。(0はbyte型の範囲なので問題なし)
uint u = 0; // ← intの0だけど、左辺がuint型のため暗黙的に0をuint型とみなす。(0はuint型の範囲なので問題なし)

つまり、定数値0はint型とみなされます。(型指定子の指定がない場合)
そして、int型の0の否定は同様にint型の-1とみなされます。int型からbyte型やuint型に対しては暗黙的な型変換が
安全に行える保障がないためエラーとなります。それに対してuint型やulong型に対してはバイトサイズで問題がないため
暗黙的に型変換が行える仕様になっているものと考えられます。
そのため、ご質問の結果となるのではないでしょうか。

#たぶん合っていると思うのですが、言語仕様書の××章に書いてあると明示できていないので、眉つばでお願いします。


引用返信 編集キー/
■35412 / inTopicNo.3)  Re[2]: enumの型指定について
□投稿者/ pirosuke (2回)-(2009/04/29(Wed) 03:48:27)
No35411 (よねKEN さん) に返信
> 言語仕様書の列挙体の箇所では特に言及されていないようなので(見落としているかもしれませんが)、
> 通常の定数値やその演算結果の扱いと同じと考えられます。

言語仕様は完全に見落としていました・・・

> つまり、定数値0はint型とみなされます。(型指定子の指定がない場合)
> そして、int型の0の否定は同様にint型の-1とみなされます。int型からbyte型やuint型に対しては暗黙的な型変換が
> 安全に行える保障がないためエラーとなります。それに対してuint型やulong型に対してはバイトサイズで問題がないため
> 暗黙的に型変換が行える仕様になっているものと考えられます。
> そのため、ご質問の結果となるのではないでしょうか。

この考えからすると
uint ui = -1;
のような記述が可能であるという認識で問題ないでしょうか?

ですが、実際に上記コードはコンパイルエラーになります。

intからuintに変換する際にも明示的にキャストを行わなければならないため、
今回のケースで暗黙的に型変換が行われるというのは考え難いと思っております。

そのような点から型指定したenum値は指定した型と同等の扱いになり、
符号なし整数型の0の否定は最大値になるものだと思っていましたが、
なぜかbyteとushortがエラーになったので疑問に感じました。
(言語仕様を調べてないので実際には何とも言えませんが。。。)

> #たぶん合っていると思うのですが、言語仕様書の××章に書いてあると明示できていないので、眉つばでお願いします。

いえいえ、とんでもございません。
返信があるだけで十分ありがたいです。

では、言語仕様の点からenumの型指定と否定がどういった扱いになるのか調べて見ますね。
英語読めないですが気合で何とか・・・

以上です。
ありがとうございました。
引用返信 編集キー/
■35413 / inTopicNo.4)  Re[3]: enumの型指定について
□投稿者/ pirosuke (3回)-(2009/04/29(Wed) 05:39:39)
言語仕様を調べたところ、以下のことがわかりました。
調べた言語仕様書はVersion 1.2です。

1.型指定した場合は、enumの値は指定した型のサイズで確保される。
→1.10 Enumsより

つまり、指定した型の扱いになるということらしい。

2.~演算子をenumに使用した場合、「(列挙型)(~(指定した型)値)」の形になる。
・7.6.4 Bitwise complement operatorより

例の場合、(test)(~(byte)None) となるようです。
色々試した結果byte型やushort型に~演算子を使用するとint型に変換されるようです。
そのため0の否定が255ではなく-1になったとのことです。
自分の英語力不足から、byte型等の~演算子使用時の型変換について記述を見つけることができませんでした。
どなたかご存知の方がおられれば段落だけでも教えて頂ければ幸いです。
暫く反応がないようでもそのまま解決済みにしたいと思います。

以上、よろしくお願いいたします。
引用返信 編集キー/
■35415 / inTopicNo.5)  Re[4]: enumの型指定について
□投稿者/ 魔界の仮面弁士 (1053回)-(2009/04/29(Wed) 13:28:19)
2009/04/29(Wed) 13:44:32 編集(投稿者)

No35413 (pirosuke さん) に返信
> 2.~演算子をenumに使用した場合、「(列挙型)(~(指定した型)値)」の形になる。
> ・7.6.4 Bitwise complement operatorより
> 例の場合、(test)(~(byte)None) となるようです。

列挙型 に対しては、そのようになると書かれていますね。

ただし 7.6.4 には、この演算子で定義済みであるものが、
int / uint / long / ulong であると書かれています。
byte / ushort に関しては記載がありません。

この場合、単項演算子のパラメータの型変換が発生するようなので、
最も近しい int に変換されていたのでは無いでしょうか。


ちなみに、
 public enum test : byte
 {
  None = 0,
  All = unchecked((byte)~None),
 }
のように宣言する事は可能です。


> 自分の英語力不足から、byte型等の~演算子使用時の型変換について記述を見つけることができませんでした。
英語版を読むのが辛いのであれば、先に日本語版に目を通しておくとか。

C:\Program Files\Microsoft Visual Studio 8\VC#\Specifications\1041\C# Language Specification 1.2.doc
引用返信 編集キー/
■35416 / inTopicNo.6)  Re[5]: enumの型指定について
□投稿者/ 魔界の仮面弁士 (1054回)-(2009/04/29(Wed) 13:38:03)
2009/04/29(Wed) 14:26:29 編集(投稿者)
No35415 (魔界の仮面弁士) に追記
> ただし 7.6.4 には、この演算子で定義済みであるものが、
> int / uint / long / ulong であると書かれています。
> byte / ushort に関しては記載がありません。
> この場合、単項演算子のパラメータの型変換が発生するようなので、
> 最も近しい int に変換されていたのでは無いでしょうか。

実装状況を追試してみました。

Console.WriteLine((~(byte  )0).GetType().FullName);    // int  (System.Int32 )
Console.WriteLine((~(sbyte )0).GetType().FullName);    // int  (System.Int32 )
Console.WriteLine((~(ushort)0).GetType().FullName);    // int  (System.Int32 )
Console.WriteLine((~(short )0).GetType().FullName);    // int  (System.Int32 )
Console.WriteLine((~(uint  )0).GetType().FullName);    // uint (System.UInt32)
Console.WriteLine((~(int   )0).GetType().FullName);    // int  (System.Int32 )
Console.WriteLine((~(ulong )0).GetType().FullName);    // ulong(System.UInt64)
Console.WriteLine((~(long  )0).GetType().FullName);    // long (System.Int64 )


という事で、単項演算子「~」の実装は、今のところ、
int / uint / long / ulong しか存在していないようです。

---
【追記】
C# プログラマーズ リファレンスにも、下記の記載がありますね。

http://msdn.microsoft.com/ja-jp/library/d2bd4x66.aspx
》ビットごとの補数演算子は、int、uint、long、および ulong に対して組み込まれています。

引用返信 編集キー/
■35417 / inTopicNo.7)  Re[6]: enumの型指定について
□投稿者/ pirosuke (4回)-(2009/04/29(Wed) 14:30:06)
No35415 (魔界の仮面弁士 さん) に返信
> ただし 7.6.4 には、この演算子で定義済みであるものが、
> int / uint / long / ulong であると書かれています。
> byte / ushort に関しては記載がありません。
>
> この場合、単項演算子のパラメータの型変換が発生するようなので、
> 最も近しい int に変換されていたのでは無いでしょうか。

投稿後も少し翻訳作業を続けてて、
なんとなくそうなのかなとは思ってましたが、
眠気に負けて倒れてました^^;

> ちなみに、
>  public enum test : byte
>  {
>   None = 0,
>   All = unchecked((byte)~None),
>  }
> のように宣言する事は可能です。

uncheckedなんてあったんですか。
調べたら知らないステートメントがたくさん。
勉強不足ですね。申し訳ないです。

>>自分の英語力不足から、byte型等の~演算子使用時の型変換について記述を見つけることができませんでした。
> 英語版を読むのが辛いのであれば、先に日本語版に目を通しておくとか。
>
> C:\Program Files\Microsoft Visual Studio 8\VC#\Specifications\1041\C# Language Specification 1.2.doc

日本語版があったんですか・・・
msdnに英語版しかなかったんでこれしかないのかと思ってました。
早速一通り読んでみます。

> 実装状況を追試してみました。

より詳細な内容、ありがとうございます。
なるほど、確かに4種類しかありませんね。
byte / sbyte / ushort / shortは全てintになってしまうんですか。
仕様書に明記されてるなら仕方ないですが、せめて符号のあるなしは判定してほしかった・・・

スッキリしました。
回答を下さったよねKENさん、魔界の仮面弁士さんありがとうございました。


解決済み
引用返信 編集キー/
■35418 / inTopicNo.8)  Re[5]: enumの型指定について
□投稿者/ よねKEN (325回)-(2009/04/29(Wed) 14:38:28)
私の解釈は間違っていたようです。申し訳ありません。

> uint ui = -1;
uint ui = 0xffff;
ならできますが、-1の代入はもちろんできませんねm(_ _)m。ご指摘の通りです。

No35415 (魔界の仮面弁士 さん) に返信
> ■No35413 (pirosuke さん) に返信
>>2.~演算子をenumに使用した場合、「(列挙型)(~(指定した型)値)」の形になる。
>>・7.6.4 Bitwise complement operatorより
>>例の場合、(test)(~(byte)None) となるようです。
>
> 列挙型 に対しては、そのようになると書かれていますね。
>
> ただし 7.6.4 には、この演算子で定義済みであるものが、
> int / uint / long / ulong であると書かれています。
> byte / ushort に関しては記載がありません。

こちらの内容を踏まえると
byte型の列挙体の場合、

None = 0, ← Noneはbyte型の0
All = ~None, ← Noneはbyte型の0だが、~演算子はbyte型には定義されていないため、
        ~演算子が定義されている最も小さい型のintであると解釈される。
        ~Noneの結果は0xFFFF(十進表記で-1)のため、byte型には代入できず、エラーになる。

uint型の列挙体の場合、
None = 0, ← Noneはuint型の0
All = ~None, ← Noneはuint型の0で、~演算子はuint型には定義されてるため、
        演算結果は、uintの0xFFFF(十進表記で65535)となり、エラーにならない。

ということのようですね。

引用返信 編集キー/
■35421 / inTopicNo.9)  Re[7]: enumの型指定について
□投稿者/ 魔界の仮面弁士 (1055回)-(2009/04/29(Wed) 18:03:22)
2009/04/29(Wed) 18:18:46 編集(投稿者)
よねKENさんの投稿で解決チェックが外れているので、
再チェックがてら、蛇足情報を追記。


■No35417 (pirosuke さん) に返信
>>>自分の英語力不足から、byte型等の~演算子使用時の型変換について記述を見つけることができませんでした。
>>英語版を読むのが辛いのであれば、先に日本語版に目を通しておくとか。
>>C:\Program Files\Microsoft Visual Studio 8\VC#\Specifications\1041\C# Language Specification 1.2.doc
> 日本語版があったんですか・・・
> msdnに英語版しかなかったんでこれしかないのかと思ってました。

英語版 Visual Studio をインストールしている場合には、VC#\Specifications\1033、
日本語版Visual Studio をインストールしている場合には、VC#\Specifications\1041 に、
それぞれ言語仕様書が入っていますね。

ダウンロード センターからは、この程度しか見つかりませんでした。

[C# バージョン 3.0 仕様 2005年9月]
http://download.microsoft.com/download/B/6/C/B6C2DA74-08F9-4B18-BB10-CF6DB1A5CFE2/csharp_30_specification.doc

[C# Language Specification Version 3.0]
http://download.microsoft.com/download/3/8/8/388e7205-bc10-4226-b2a8-75351c669b09/csharp%20language%20specification.doc

[C# Version 3.0 Specification September 2005]
http://download.microsoft.com/download/9/5/0/9503e33e-fde6-4aed-b5d0-ffe749822f1b/csharp%203.0%20specification.doc

[C# Version 2.0 Specification September 2005]
http://download.microsoft.com/download/9/8/f/98fdf0c7-2bbd-40d3-9fd1-5a4159fa8044/CSharp%202.0%20Specification_Sept_2005.doc

[C# Language Specification Version 1.2]
http://download.microsoft.com/download/5/e/5/5e58be0a-b02b-41ac-a4a3-7a22286214ff/csharp%20language%20specification%20v1.2.doc

[C# Language Specification Version 1.0]
http://download.microsoft.com/download/a/9/e/a9e229b9-fee5-4c3e-8476-917dee385062/CSharp%20Language%20Specification%20v1.0.doc



> uncheckedなんてあったんですか。
> 調べたら知らないステートメントがたくさん。

この場合の unchecked は演算子です。unchecked ステートメントの方は、
 unchecked
 {
  byte x = (byte)-1;
 }
というブロック構造を取ります。言語仕様書でいうとこのあたり。

 5.3.3.2 Block statements, checked, and unchecked statements
 5.3.3.2 ブロック ステートメント、checked ステートメント、および unchecked ステートメント

 7.5.12 The checked and unchecked operators
 7.5.12 checked 演算子と unchecked 演算子

 8.11 The checked and unchecked statements
 8.11 checked ステートメントと unchecked ステートメント

コンパイラオプションでの指定も可能です。
http://msdn.microsoft.com/ja-jp/library/h25wtyxf.aspx



ちなみに、言語仕様書には記載されていないキーワードも幾つか存在します。
これらは、Visual Studio の C# エディタ上では、予約語の色(通常は青)で表示されます。
 __arglist
 __makeref
 __reftype
 __refvalue


using System;
class Program
{
 static void Main()
 {
  // System.Console クラスの、下記のオーバーロードを呼び出す。
  // public static void WriteLine(string format, object arg0, object arg1, object arg2, object arg3, __arglist)
  Console.WriteLine("{0},{1},{2},{3},{4},{5},{6}", 10, 11, 12, 13, __arglist(20, 21, 22));

  Console.WriteLine("---可変長引数リスト---");
  Foo1(__arglist(DateTime.Now, "test", 0x1234));

  Console.WriteLine("---型付き参照---");
  Foo2(new Random().Next());

  Console.ReadKey();
 }

 static void Foo1(__arglist)
 {
  ArgIterator argIterator = new ArgIterator(__arglist);
  while (argIterator.GetRemainingCount() > 0)
  {
   Console.WriteLine("\t{0}", TypedReference.ToObject(argIterator.GetNextArg()));
  }
 }

 static void Foo2<T>(T arg)
 {
  TypedReference tr = __makeref(arg);
  Type type = __reftype(tr);
  T val = __refvalue(tr, T);

  Console.WriteLine("\tType = {0} / {1}", typeof(T), type);
  Console.WriteLine("\tValue= {0} / {1}", arg, val);
 }
}

解決済み
引用返信 編集キー/
■35428 / inTopicNo.10)  Re[6]: enumの型指定について
□投稿者/ pirosuke (5回)-(2009/04/29(Wed) 20:25:30)
No35418 (よねKEN さん) に返信
> byte型の列挙体の場合、
>
> None = 0, ← Noneはbyte型の0
> All = ~None, ← Noneはbyte型の0だが、~演算子はbyte型には定義されていないため、
>         ~演算子が定義されている最も小さい型のintであると解釈される。
>         ~Noneの結果は0xFFFF(十進表記で-1)のため、byte型には代入できず、エラーになる。
>
> uint型の列挙体の場合、
> None = 0, ← Noneはuint型の0
> All = ~None, ← Noneはuint型の0で、~演算子はuint型には定義されてるため、
>         演算結果は、uintの0xFFFF(十進表記で65535)となり、エラーにならない。
>
> ということのようですね。

そのようですね。
大変わかりやすくまとめて頂きありがとうございます。

やっぱり日々勉強ですね・・・
まぁ、それが楽しくもありますがw
解決済み
引用返信 編集キー/
■35429 / inTopicNo.11)  Re[8]: enumの型指定について
□投稿者/ pirosuke (6回)-(2009/04/29(Wed) 20:50:13)
No35421 (魔界の仮面弁士 さん) に返信
> 英語版 Visual Studio をインストールしている場合には、VC#\Specifications\1033、
> 日本語版Visual Studio をインストールしている場合には、VC#\Specifications\1041 に、
> それぞれ言語仕様書が入っていますね。

おかげ様で翻訳作業から解放されました。
ですが今回の件で、今まで以上にプログラマとして英語の必要性を感じました。
プログラムで優秀な書籍は洋書が多いですしね・・・

> ダウンロード センターからは、この程度しか見つかりませんでした。

3.0の日本語版があるなら他もあるはずと思っていましたが、やはりこの程度しか見つかりませんか・・・

> この場合の unchecked は演算子です。unchecked ステートメントの方は、
>  unchecked
>  {
>   byte x = (byte)-1;
>  }
> というブロック構造を取ります。言語仕様書でいうとこのあたり。

演算子とステートメントの違いですか・・・
度々不勉強で申し訳ないです。

> ちなみに、言語仕様書には記載されていないキーワードも幾つか存在します。
> これらは、Visual Studio の C# エディタ上では、予約語の色(通常は青)で表示されます。
>  __arglist
>  __makeref
>  __reftype
>  __refvalue
>

む、難しい・・・
__arglistは便利そうですね。
その他は値型を参照型っぽく使いたい場合に使用するのかな?
大変勉強になります。


改めて、回答を下さった皆様大変ありがとうございました。
解決済み
引用返信 編集キー/
■35430 / inTopicNo.12)  Re[9]: enumの型指定について
□投稿者/ 魔界の仮面弁士 (1056回)-(2009/04/29(Wed) 21:42:25)
蛇足ついでに。

No35429 (pirosuke さん) に返信
> __arglistは便利そうですね。
や、まったくと言って良いほど出番は無いと思います。

__arglist は CLS 非準拠ですし、そもそもヘルプにも記載がありません。
通常は、__arglist ではなく、params を使った方が便利でしょう。
http://msdn.microsoft.com/ja-jp/library/ms182366.aspx

ちなみに可変引数リスト[__arglist] は、C/C++ では [...] と記述されます。
VB では、[__arglist]はサポートされませんが、[params]に相当する[ParamArray]が利用できます。


> その他は値型を参照型っぽく使いたい場合に使用するのかな?
__makeref 等は、先の __arglist と共に使われるものです。

ArgIterator クラスから得られた個々の要素は、
TypedReference クラスとして得られることになり、その操作に利用できます。

ちなみに、値の取り出しは
 T val = __refvalue(tr, T);
ですが、値の設定は、
 __refvalue(tr, T) = val;
です。とはいえ、普段使い道が無いので、覚えても役に立たないこと請け合い。

# これらの C# 予約語は、SSCLI 2.0 の csharp\inc\tokens.h ファイルにも記載がありますね。
# http://www.microsoft.com/downloads/details.aspx?FamilyId=8C09FD61-3F26-4555-AE17-3121B4F51D4D&displaylang=en
解決済み
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -