« [NEWS] 温室効果ガス削減、原発頼み鮮明に | トップページ | [.NET] Enlist=false でも、 DataContext は分散トランザクションの昇格をしちゃう »

2008年11月14日 (金)

Re: 続:.NET FrameworkのCultureと"々"

おくればせながら、 元ネタをよーやく検証しました。

( もちろん ! ) 再現できたので、 Visual Studio & .Net Framework プロダクト フィードバック センターに vote / 検証 / 回避策 それぞれ ++ してきました。

> 一日でトップアクティブバグレポートに躍り出るとは思いませんでした。

私が出した WPF ネタが長らくトップだったのに。 残念じゃ~ (^^;

> なんかわざとやっているのかと思います。

わざとかどうかは別として、 とりあえずは IndexOf() だけみたいですね。
StringComparison を指定できるその他のメソッド Compare(), LastIndexOf(), StartsWith() といったあたりは、 "愛々" では大丈夫そうでした。
→ テストコード UnitTest1.cs ( 7,192 バイト, VS2008 Pro. 以上 )

IndexOf() の挙動不審のイヤなところには、 こんなのもあって…

Assert.AreEqual<int>(0, "愛々,1,2,3".IndexOf("愛"));
Assert.AreEqual<int>(1, "愛々,1,2,3".IndexOf("々"));
Assert.AreEqual<int>(1, "愛々,1,2,3".IndexOf(","));
Assert.AreEqual<int>(3, "愛々,1,2,3".IndexOf("1"));
※ "々" も "," も、 どちらも先頭から 2文字目 (inex=1) なんだそうで。

Assert.AreEqual<int>(3, "愛々愛々,1,2,3".IndexOf(",")); //先頭だけおかしくなる!?
Assert.AreEqual<int>(3, "12愛々,1,2,3".IndexOf(",")); //や、そーでもないぞ。
Assert.AreEqual<int>(4, "愛々12,1,2,3".IndexOf(",")); //なぜ合う?
Assert.AreEqual<int>(1, "愛々12,1,2,3".IndexOf("1"));
※ なんと言えばいいものやら… f(^^;

回避策としては、 コード分析 ( FxCop ) の仰せに従って、 StringComparison を指定してやれば、 大丈夫みたいです。 ( kkamegawa 氏の探し出してくれた全ての文字を検査したわけじゃないので、 万全とは言えないです。 )

"愛々,1,2,3".IndexOf(",") というコードに対して、 コード分析 ( FxCop ) は CA1307 という警告をしてきます。

CA1307: StringComparison を指定します

多くの文字列演算 ( 最も重要なものは Compare メソッドおよび Equals メソッド) では、 StringComparison 列挙体をパラメータとして受け取るオーバーロードが用意されています。

StringComparison パラメータを受け取るオーバーロードが存在する場合は、 このパラメータを受け取らないオーバーロードではなく、 受け取るオーバーロードを使用する必要があります。

言われた通りに、 "愛々,1,2,3".IndexOf(",", StringComparison.Ordinal) としてやれば、 この問題は出なくなるようです。 また、 CA1309 の解説には次のように書かれています。

StringComparison.Ordinal または StringComparison.OrdinalIgnoreCase を指定すると、 文字列比較は非言語的になります。 つまり、 比較を判断する場合に、 自然言語固有の機能は無視されます。 これは、 判断は単純なバイト比較に基づいたものであることを示し、 大文字と小文字の指定、 またはカルチャでパラメータ化される同等の表は無視されます。 その結果、 パラメータを StringComparison.Ordinal または StringComparison.OrdinalIgnoreCase に明示的に設定することによって、 多くの場合、 コードの速度、 正確さ、 および信頼性が向上します。

スピードも上がるそうですから、 今回の回避策とは関係なく、 カルチャに関係無い比較で良い場合は積極的に指定した方がよさそうですね。

|

« [NEWS] 温室効果ガス削減、原発頼み鮮明に | トップページ | [.NET] Enlist=false でも、 DataContext は分散トランザクションの昇格をしちゃう »

プログラミング」カテゴリの記事

コメント

もひとつ。

CA1307 の警告が言ってるのは、 IndexOf() なら引数 2つのバージョンを使え、 ということであって、 StringComparison.Ordinal を指定しろ、 ではありません。

これも、 私の書き方が悪かったわけですが、 Ordinal か OrdinalIgnoreCase を指定しろ、 と警告してくるのは、 CA1309 「非言語的な文字列比較演算で、StringComparison パラメータが Ordinal または OrdinalIgnoreCase に設定されていません。」 です。

StringComparison が指定されていないと CA1307 が。 指定されているけど、 そこはカルチャに依存しない比較でいいんじゃないってとこには CA1309 が出る、 ということです。

投稿: biac@会社 | 2008年11月15日 (土) 12時51分

おっといけない。 うちのプロジェクトでやんなきゃいけないこと、 に気を取られて、 乱暴な書き方になってしまいました m(_`_)m

ということで、 最後の一文中に 「カルチャに関係無い比較で良い場合は」 を追加しました。

・カルチャに関係無い比較で良い場合は、 StringComparison.Ordinal または StringComparison.OrdinalIgnoreCase を指定するべき。

・カルチャによる比較をさせる場合は、 引数 1個の IndexOf() を使うのではなくて、 引数 2個のものを使って、 明示的に StringComparison.CurrentCulture などを指定するべき。 ( たとえ CurrentCulture で良かったとしても、 プログラマの意思を明確化しておく。 )
※ ただし、 今回の不具合に引っ掛かる。 ( たぶん )

投稿: biac@会社 | 2008年11月15日 (土) 12時44分

追加健勝ありがとうございます。そう他のStringの文字列切り出し系メソッドもやりたかったんですが…。

ただ、はやくなるし、FxCopで進められているからStringComparison.Ordinalを指定すれば良いってのはどうなんでしょう。

このあたりはI18nとからんでどうすればいいのか自分の中で消化できていません。

投稿: kkamegawa | 2008年11月15日 (土) 11時49分

この記事へのコメントは終了しました。

トラックバック


この記事へのトラックバック一覧です: Re: 続:.NET FrameworkのCultureと"々":

« [NEWS] 温室効果ガス削減、原発頼み鮮明に | トップページ | [.NET] Enlist=false でも、 DataContext は分散トランザクションの昇格をしちゃう »