- 投稿日:2020-10-20T23:39:44+09:00
[C#]演算子のオーバーロードとプリミティブ型
はじめに
演算子のオーバーロードはop_xxxというメソッドが内部で定義されるようですが、int型などはこのメソッドを持っていません。
ILの仕様と言われればそれまでですが、ILの仕様を熟知していないので、とりあえず実験してみました。
Type.IsPrimitiveと何か関係があるのかなと思い、組み合わせて検証しました。いい感じの検索ワードが思いつかず検証してみたので、(なんの役に立つのかはわかりませんが、)自分用のメモとして残します。
環境
Windows 10
Visual Studio 2019参考
プリミティブ型について
https://ufcpp.net/blog/2016/12/tipsprimitives/検証1
OP.csforeach (var t in types) { bool haveOverloadOperator = t.GetMethods().Count(x => x.Name.StartsWith("op_")) > 0; Console.WriteLine($"{t.Name} : {t.IsPrimitive} : {haveOverloadOperator}"); }Boolean : True : False Byte : True : False SByte : True : False Int16 : True : False UInt16 : True : False Int32 : True : False UInt32 : True : False Int64 : True : False UInt64 : True : False IntPtr : True : True UIntPtr : True : True Char : True : False Double : True : True Single : True : True Decimal : False : True Object : False : False String : False : True DateTime : False : True TimeSpan : False : Trueop_から始まるメソッドを持っているかどうかのチェックですが、Int16などの整数型はメソッドop_で始まるメソッドがないという、少し意外な結果となりました。
検証2
OP.csforeach (var t in types) { List<MethodInfo> OverloadOperators = t.GetMethods().Where(x => x.Name.StartsWith("op_")).ToList(); foreach (var m in OverloadOperators) { Console.WriteLine($"{t.Name} : {m.Name}"); } }IntPtr : op_Explicit IntPtr : op_Explicit IntPtr : op_Explicit IntPtr : op_Explicit IntPtr : op_Explicit IntPtr : op_Explicit IntPtr : op_Equality IntPtr : op_Inequality IntPtr : op_Addition IntPtr : op_Subtraction UIntPtr : op_Explicit UIntPtr : op_Explicit UIntPtr : op_Explicit UIntPtr : op_Explicit UIntPtr : op_Explicit UIntPtr : op_Explicit UIntPtr : op_Equality UIntPtr : op_Inequality UIntPtr : op_Addition UIntPtr : op_Subtraction Double : op_Equality Double : op_Inequality Double : op_LessThan Double : op_GreaterThan Double : op_LessThanOrEqual Double : op_GreaterThanOrEqual Single : op_Equality Single : op_Inequality Single : op_LessThan Single : op_GreaterThan Single : op_LessThanOrEqual Single : op_GreaterThanOrEqual Decimal : op_Implicit Decimal : op_Implicit Decimal : op_Implicit Decimal : op_Implicit Decimal : op_Implicit Decimal : op_Implicit Decimal : op_Implicit Decimal : op_Implicit Decimal : op_Implicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_Explicit Decimal : op_UnaryPlus Decimal : op_UnaryNegation Decimal : op_Increment Decimal : op_Decrement Decimal : op_Addition Decimal : op_Subtraction Decimal : op_Multiply Decimal : op_Division Decimal : op_Modulus Decimal : op_Equality Decimal : op_Inequality Decimal : op_LessThan Decimal : op_LessThanOrEqual Decimal : op_GreaterThan Decimal : op_GreaterThanOrEqual String : op_Equality String : op_Inequality DateTime : op_Addition DateTime : op_Subtraction DateTime : op_Subtraction DateTime : op_Equality DateTime : op_Inequality DateTime : op_LessThan DateTime : op_LessThanOrEqual DateTime : op_GreaterThan DateTime : op_GreaterThanOrEqual TimeSpan : op_UnaryNegation TimeSpan : op_Subtraction TimeSpan : op_UnaryPlus TimeSpan : op_Addition TimeSpan : op_Equality TimeSpan : op_Inequality TimeSpan : op_LessThan TimeSpan : op_LessThanOrEqual TimeSpan : op_GreaterThan TimeSpan : op_GreaterThanOrEqual列挙してみると、キャストも含まれていました。。。
doubleやstringに、足し算を意味する"op_Addition"がありません。
整数や浮動小数点数はオーバーロードではなく、コンパイラがILの専用命令に変換しているからop_Additionは不要のようです。
また、参考のリンク先によるとstringの+は文字列連結のconcatに変換されるらしいので、オーバーロードとして定義されていません。追伸
ここまでくれば、ILの仕様もしくはコンパイル後のILを覗いてみればいいような気がします。
機会があればILを見てみたいと思います。
- 投稿日:2020-10-20T20:53:05+09:00
C#におけるcharは整数型なのか?
はじめに
『なるほどなっとくC#入門』(はじめてプログラミングを学ぶ人を対象にしたC#の入門書)の読者の方から、こんな指摘がきました。1
C#組み込み型の表でcharが文字型とされていますが正しくは整数型です
https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/language-specification/types確かにこのページには、'char'は integral_typeに分類されています。
この事実は僕も知っていました。実際、以下のようなコードはビルドエラーにはならず、実行することができます。
char a = 'a'; char b = 'c'; System.Console.WriteLine(a + b); System.Console.WriteLine(a * b);といっても、結果は、char型にはなってくれませんが...
C言語でバリバリプログラミングしていた頃は、charが整数型であることを利用したコードもよく書いていました。
しかし、「なるほどなっとくC#入門」では、組み込み型の説明では、整数型とは独立させて、charを以下の表のように文字型として分類しました。
組み込み型の種類 キーワード 整数型 byte, byte, short, short, int, uint, long, long 文字型 char .... .... 以下略 ... なぜ、文字型として分類したのか
『なるほどなっとくC#入門』の「はじめに」で僕は以下のように書いています。
文法の解説では、できるだけ正確な表現になるように努めましたが、正確で詳細な文法解説は、退屈であると同時に無用な混乱を招いてしまう恐れがあります。そのため、わかりやすさと正確さのバランスをとりながら説明するようにしました。
つまり、charを整数型に分類し説明することは、初心者には無用な混乱を招くと判断したわけです。
実際に、char型を整数型として扱う場面はほんのわずかであり、整数型とは独立したchar型として説明するほうが、理解が進むというのが僕の考えです。
Microsoft Docsの他のページではどう説明されているのか
これだけでは、僕の独りよがりみたいな意見で終わってしまうので、Microsoft Docsの他のページでは、どのように説明されているのか見てみましょう。
値型 (C# リファレンス) では、以下のように説明されています。
C# には、単純型とも呼ばれる次の組み込み値型が用意されています。
整数数値型
浮動小数点数値型
ブール値を表す bool
Unicode UTF-16 文字を表す charchar型は、整数型とは別に独立させています。
また、C# 型の既定値 (C# リファレンス)では、以下のような記述があります。
種類 既定値 任意の組み込み整数数値型 0 (ゼロ) char '\0' (U+0000) 整数数値型 (C# リファレンス)では、以下のような記述があります。
C# では、次の定義済みの整数型がサポートされています。
C# 型/キーワード 範囲 サイズ .NET 型 sbyte -128 ~ 127 符号付き 8 ビット整数 System.SByte byte 0 ~ 255 符号なし 8 ビット整数 System.Byte short -32,768 ~ 32,767 符号付き 16 ビット整数 System.Int16 ushort 0 ~ 65,535 符号なし 16 ビット整数 System.UInt16 int -2,147,483,648 ~ 2,147,483,647 符号付き 32 ビット整数 System.Int32 uint 0 ~ 4,294,967,295 符号なし 32 ビット整数 System.UInt32 long -9,223,372,036,854,775,808 から 9,223,372,036,854,775,807 符号付き 64 ビット整数 System.Int64 ulong 0 ~ 18,446,744,073,709,551,615 符号なし 64 ビット整数 System.UInt64 この表にはcharは含まれていません。
これらのページでは、charは整数型とは別の型であるという立場をとっているように見えます。
結論
ということで、僕の結論は以下の通りです。
C#の言語仕様的には、char型は整数型に分類されているが、C#を説明する上では、charを整数型とは別の型として説明することは間違いではなく、むしろchar型は整数型とは別の型として説明するべきものである。
もちろん、C#の仕様を理解したエンジニアのかたが、「char型は整数型である」と主張することに異論を唱えるつもりはありません。
ある程度、理解が進んだ方には、「char型は算術演算が可能でint型の結果が生成される」ということを知ることも有意義だと思います。
ただ、C#を教えるという立場からすると、経験の浅いプログラマーが、嬉々としてcharを整数型として使うようなケースが出てくることのほうが怖いなーと思ったりもしてしまいます。
出版社経由の匿名の読者様ということで直接僕の考えをお伝えすることはできませんでした。 ↩