2009/8/17 (月)
■[C#][設計] .NET Framework に設計を学ぶ : メソッド名の頻出接頭辞
名前大事。間違いなく大事。名前大事を逆手にとって、名前をランダムに改変して読めなくするツールがあることからも、名前がいかに大事かわかります。
名前大事はわかるけど、うまい名前が浮かばないことがよくあって、そういうときはメソッドがうまく設計できていないときだったりもします。じゃあ、きれいに設計されている .NET Framework を調べて、名前付け/設計の極意を学んでみようと思いついたのでやってみました。
あまり手を広げると大変なので、今日はメソッド名の頻出接頭辞を調べてみました。
MSDN にあるメソッド名のガイドラインから抜粋
- .NET ではメソッド名は ToString のように Pascal 形式で名前を付けます
- パラメータ名などには typeName のように Camel 形式を使います
- メソッド名には動詞または動詞句(うーん、苦手…)を使います
- 通常、メソッドはデータを操作するため、メソッドのアクションを表す動詞を使用すると、開発者にはメソッドの機能がよりわかりやすくなります
- メソッド名には実装の詳細を使用しないでください
- how をメソッド名にするな、what をメソッド名にしろってことですね
RemoveAll, GetCharArray, Invoke などが正しい例として挙げられています。
メソッド名のパターン
例えば、.NET では Int32 にも Byte にも TryParse という似たようなメソッドがあって経験が活かせるように配慮してあります。同じように自作のクラスにも文字列を解析して、成功したらオブジェクトを返すメソッドを作るなら TryParse の名前を付けて、引数の形式もまねれば、.NET の経験がある人なら迷わずに使えます。
ちなみにこれは TryParse パターンとして MSDN に載ってます。例外を返す Parse と例外を返さない TryParse という2つのメソッドを用意して、例外によるパフォーマンス低下を回避する手段だそうです。クラス作成者には例外ありなしのどっちがいいかわからないことがよくあるので、両方用意するならこのパターンを使えってことですね。
また、文書化されてるかわかりませんが、ApplySort と ApplySortCore のように、一つのメソッドを何らかの理由で二つにわけないといけない場合、接尾辞として Core を付けるというパターンもあります。
その他。
- イベントを発生させるメソッドの接頭辞 OnEventName
- 非同期パターン接頭辞 Beginなんとか、Endなんとか
- 特殊なメソッド名(?) Dispose, Close
- どこからどこを特殊とするか迷いますが、これらは C# では using による言語サポートがあり、通常のメソッド名とはなんとなく区別したい
接頭辞 Top30
どこまで厳密に分析するか考えるだけで何日もかかりそうなので、テキトーな感じでやりました。調べるのに使ったソースコードは最後に載せます。対象アセンブリもコードを見てください。
頻出順に「出現回数 : 割合 : 累積割合 : 接頭辞」。一段下げてその接頭辞を持つメソッド名の頻出順に「出現回数 : 割合 : メソッド名」としました(2009/8/18 追記: 割合と累積の割合を追加しました)。
やはり Get は圧倒的です。Render は意外ですが、そのほかは役に立ちそうです。
* Methods (29,496) 5125 : 17.38% : 17.38% : Get 392 : 1.33% : GetHashCode 269 : 0.91% : GetEnumerator 114 : 0.39% : GetObjectData 89 : 0.30% : GetAsFrozenCore 88 : 0.30% : GetCurrentValueAsFrozenCore 2799 : 9.49% : 26.86% : On 82 : 0.28% : OnPreRender 65 : 0.22% : OnCreateAutomationPeer 63 : 0.21% : OnInit 44 : 0.15% : OnKeyDown 34 : 0.12% : OnChanged 1295 : 4.39% : 31.26% : Create 236 : 0.80% : CreateInstanceCore 71 : 0.24% : Create 52 : 0.18% : CreateNewElement 39 : 0.13% : CreatePermission 37 : 0.13% : CreateAccessibilityInstance 1058 : 3.59% : 34.84% : Add 408 : 1.38% : Add 73 : 0.25% : AddRange 36 : 0.12% : AddChild 35 : 0.12% : AddAttributesToRender 34 : 0.12% : AddText 949 : 3.22% : 38.06% : Set 28 : 0.09% : SetItem 24 : 0.08% : SetValue 18 : 0.06% : SetBoundsCore 17 : 0.06% : SetParent 16 : 0.05% : Set 895 : 3.03% : 41.09% : Remove 358 : 1.21% : Remove 203 : 0.69% : RemoveAt 22 : 0.07% : RemoveItem 17 : 0.06% : RemoveAll 13 : 0.04% : RemoveRange 850 : 2.88% : 43.98% : To 474 : 1.61% : ToString 50 : 0.17% : ToXml 18 : 0.06% : ToDateTime 12 : 0.04% : ToFourDigitYear 12 : 0.04% : ToSqlString 735 : 2.49% : 46.47% : Clone 370 : 1.25% : Clone 153 : 0.52% : CloneCurrentValue 99 : 0.34% : CloneCore 88 : 0.30% : CloneCurrentValueCore 17 : 0.06% : CloneNode 700 : 2.37% : 48.84% : Begin 506 : 1.72% : BeginInvoke 26 : 0.09% : BeginInit 10 : 0.03% : BeginEdit 10 : 0.03% : BeginRead 10 : 0.03% : BeginWrite 694 : 2.35% : 51.19% : Is 47 : 0.16% : IsDefaultAttribute 34 : 0.12% : IsSubsetOf 25 : 0.08% : IsDefined 25 : 0.08% : IsUnrestricted 15 : 0.05% : IsInputKey 686 : 2.33% : 53.52% : End 505 : 1.71% : EndInvoke 27 : 0.09% : EndInit 10 : 0.03% : EndEdit 10 : 0.03% : EndRead 10 : 0.03% : EndWrite 615 : 2.09% : 55.60% : Invoke 550 : 1.86% : Invoke 41 : 0.14% : InvokeEventHandler 8 : 0.03% : InvokeMember 3 : 0.01% : InvokeAsync 2 : 0.01% : InvokeScript 509 : 1.73% : 57.33% : Copy 330 : 1.12% : CopyTo 96 : 0.33% : Copy 53 : 0.18% : CopyFrom 15 : 0.05% : CopyProperties 2 : 0.01% : CopyToRows 392 : 1.33% : 58.66% : Equals 391 : 1.33% : Equals 1 : 0.00% : EqualsExact 383 : 1.30% : 59.96% : Clear 300 : 1.02% : Clear 21 : 0.07% : ClearItems 3 : 0.01% : ClearContainerForItemOverride 3 : 0.01% : ClearCore 3 : 0.01% : ClearSelection 381 : 1.29% : 61.25% : Can 110 : 0.37% : CanConvertFrom 107 : 0.36% : CanConvertTo 32 : 0.11% : CanConvertFromString 32 : 0.11% : CanConvertToString 19 : 0.06% : CanBuildChannelListener 372 : 1.26% : 62.51% : Convert 132 : 0.45% : ConvertTo 124 : 0.42% : ConvertFrom 36 : 0.12% : ConvertFromString 33 : 0.11% : ConvertToString 17 : 0.06% : Convert 371 : 1.26% : 63.77% : Write 50 : 0.17% : Write 29 : 0.10% : WriteTo 19 : 0.06% : WriteLine 16 : 0.05% : WriteContentTo 10 : 0.03% : WriteByte 360 : 1.22% : 64.99% : Contains 289 : 0.98% : Contains 40 : 0.14% : ContainsKey 6 : 0.02% : ContainsValue 4 : 0.01% : ContainsAudio 4 : 0.01% : ContainsFileDropList 340 : 1.15% : 66.14% : Render 128 : 0.43% : Render 26 : 0.09% : RenderContents 17 : 0.06% : RenderAttributes 13 : 0.04% : RenderBeginTag 13 : 0.04% : RenderEndTag 271 : 0.92% : 67.06% : Dispose 268 : 0.91% : Dispose 1 : 0.00% : DisposeCore 1 : 0.00% : DisposeLocalCopyOfClientHandle 1 : 0.00% : DisposeObject 271 : 0.92% : 67.98% : Reset 135 : 0.46% : Reset 10 : 0.03% : ResetModified 9 : 0.03% : ResetAccessRule 8 : 0.03% : ResetForeColor 7 : 0.02% : ResetBackColor 269 : 0.91% : 68.89% : Index 243 : 0.82% : IndexOf 16 : 0.05% : IndexOfKey 2 : 0.01% : IndexOfValue 1 : 0.00% : IndexFromContainer 1 : 0.00% : IndexFromGeneratorPosition 256 : 0.87% : 69.76% : Read 49 : 0.17% : Read 11 : 0.04% : ReadByte 7 : 0.02% : ReadOnly 7 : 0.02% : ReadString 7 : 0.02% : ReadXml 249 : 0.84% : 70.60% : Insert 190 : 0.64% : Insert 30 : 0.10% : InsertItem 5 : 0.02% : InsertAfter 5 : 0.02% : InsertBefore 3 : 0.01% : InsertAt 221 : 0.75% : 71.35% : Initialize 84 : 0.28% : Initialize 38 : 0.13% : InitializeFrom 17 : 0.06% : InitializeLifetimeService 13 : 0.04% : InitializeCell 9 : 0.03% : InitializeDefault 198 : 0.67% : 72.02% : Load 66 : 0.22% : LoadViewState 31 : 0.11% : LoadPostData 28 : 0.09% : Load 17 : 0.06% : LoadControlState 8 : 0.03% : LoadAdapterState 158 : 0.54% : 72.56% : From 47 : 0.16% : FromXml 6 : 0.02% : FromXmlString 5 : 0.02% : FromHandle 3 : 0.01% : FromElement 3 : 0.01% : FromString 156 : 0.53% : 73.09% : Find 32 : 0.11% : Find 13 : 0.04% : FindAll 9 : 0.03% : FindName 7 : 0.02% : FindControl 6 : 0.02% : FindUsersByEmail 151 : 0.51% : 73.60% : Save 61 : 0.21% : SaveViewState 28 : 0.09% : Save 17 : 0.06% : SaveControlState 8 : 0.03% : SaveAdapterState 4 : 0.01% : SaveAs
ソースコード
自由にいじって独自の分析をし、ネットで発表してもらえるとうれしいです。
.NET フレームワークをこんなふうに分析して問題ないの?って点は、おそらく問題ないです。メソッド名は MSDN ライブラリで公開されている情報です。また、リフレクションがリバースエンジニアリングとも思えません。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; public static class Reflector { public static void Main() { string[] asmNames = { "mscorlib", "System", "System.Core", "System.Data", "System.Data.DataSetExtensions", "System.Deployment", "System.Drawing", "System.Windows.Forms", "System.Xml", "System.Xml.Linq", "PresentationCore", "PresentationFramework", "WindowsBase", "System.Configuration", "System.EnterpriseServices", "System.Web", "System.Web.Extensions", "System.Web.Mobile", "System.Web.Services", "System.Runtime.Serialization", "System.ServiceModel", }; var methodNames = from asm in asmNames from type in Assembly.LoadWithPartialName( asm ).GetExportedTypes() where !type.IsSubclassOf( typeof( Enum ) ) from method in // このあたりをいじるといろいろ分析できる type.GetMethods( BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic ) .Where( m => !m.IsSpecialName ) // プロパティ、イベントハンドラ、演算子を取り除く .Where( m => !m.IsAssembly && !m.IsPrivate ) // internal, private を取り除く .Select( m => m.Name ) .Distinct() select method.GetMethodName(); // メソッド総数 int methodsCount = methodNames.Count(); // 整列 var frequentlyMethodNames = methodNames .Select( m => new { FullName = m, First = m.SplitCamelWord().ElementAt( 0 ) } ) .GroupBy( x => x.First, x => x, ( first, xs ) => new { First = first, Count = xs.Count(), Elements = xs .GroupBy( y => y.FullName, y => y.FullName, ( f, fs ) => new { FullName = f, Count = fs.Count() } ) .OrderByDescending( b => b.Count ) .ThenBy( b => b.FullName ) .Take( 5 ) } ) .OrderByDescending( e => e.Count ) .ThenBy( e => e.First ) .Take( 30 ); using ( var sw = new StreamWriter( "name30.txt" ) ) { sw.WriteLine( "* Assemblies ({0:n0})\n", asmNames.Length ); foreach ( var item in asmNames ) { sw.WriteLine( item ); } sw.WriteLine( "\n" ); int sum = 0; sw.WriteLine( "* Methods ({0:n0})\n", methodsCount ); foreach ( var item in frequentlyMethodNames ) { sum += item.Count; sw.WriteLine( "{0,5} : {2:p2} : {3:p2} : {1}", item.Count, item.First, ( double ) item.Count / methodsCount, ( double ) sum / methodsCount ); foreach ( var m in item.Elements ) { sw.WriteLine( "\t{0,5} : {2:p2} : {1}", m.Count, m.FullName, ( double ) m.Count / methodsCount ); } sw.WriteLine(); } } } public static IEnumerable<string> SplitCamelWord( this string str ) { if ( str.StartsWith( "get_" ) || str.StartsWith( "set_" ) || str.StartsWith( "add_" ) ) str = str.Substring( 4 ); if ( str.StartsWith( "remove_" ) ) str = str.Substring( 7 ); if ( str.StartsWith( "op_" ) ) str = str.Substring( 3 ); int i = 0, j; for ( j = 1; j < str.Length; j++ ) { if ( 'A' <= str[ j ] && str[ j ] <= 'Z' ) { yield return str.Substring( i, j - i ); i = j; } } if ( i != j ) yield return str.Substring( i, j - i ); } public static string GetMethodName( this string str ) { if ( str.Contains( "." ) ) { str = str.Split( '.' ).Last(); } return str; } }
次回は bool を返すメソッドとプロパティの接頭辞編。
参考
2009/8/18 追記: 割合と累積の割合も追加。ソースコードもあわせて変更。参考を追加(id:NyaRuRu さんありがとうございます)。
2006 | 01 | 02 | 03 | 04 | 06 | 09 | 11 | 12 |
2007 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 08 | 09 | 10 | 12 |
2009 | 01 | 03 | 04 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 07 |
2011 | 04 | 07 | 10 |
2012 | 04 | 12 |
2013 | 08 |
NyaRuRu 2009/08/18 00:05 こちらの記事もおすすめです.
http://steps.dodgson.org/?date=20090314
siokoshou 2009/08/18 08:33 おー、これはすごい!ずっと深い分析をしてるのがさすが!
参考にします。ありがとうございます(^^)
次に活かせ…るかな?(^^;
Yaju 2009/08/19 21:57 メソッド名ランキング
http://blogs.wankuma.com/youryella/archive/2007/06/17/81020.aspx
siokoshou 2009/08/19 22:53 おぉ、二年も前にやってたんですね!ネタかぶってすみません…。