トップ回答者
using(を使った命令を使わないで書くには?

質問
-
いつもお世話になります。
VisualC#2010、WindowsXPでの動作について質問があります。
http://neue.cc/2010/03/02_244.html
を参考に、using (var sgml = new SgmlReader() { Href = "http://www.xbox.com/ja-JP/community/events/Home" }) {
xml = XDocument.Load(sgml);
}を読もうとしています。
これを、using(を使わずに書くと、どうなりますか?
回答
-
SgmlReaderを読み解く前に言語構文が理解できていなかったわけですね。誤解していました。
正式に、忠実に書くなら、
{
var _sgml = new SgmlReader();
_sgml.Href = "http://www.xbox.com/ja-JP/community/events/Home";
var sgml = _sgml;
try{
xml = XDocument.Load(sgml);
}
finally{
if( _sgml != null )
((IDisposable)_sgml).Dispose();
}
}かな。ただし、_sgmlは仮の変数でソースコードからは参照できません。なぜこうなっているかというと、try{}の中でsgml変数を書き換えても、元の値に対してDispose()を呼ぶためです。
で、そうだとしたら、それが書式だといわれればそのとおりなのですが、
using (var sgml = new SgmlReader() { Href = "http://www.xbox.com/ja-JP/community/events/Home" }) {
xml = XDocument.Load(sgml);
}using (var sgml = new SgmlReader();
Href = "http://www.xbox.com/ja-JP/community/events/Home" ;
){
xml = XDocument.Load(sgml);
}
とかと書かないんでしょう?using()の中はif()やfor( ; ; )の中などと同じくexpression(日本語訳では式)しか書けません。d-kotさんの書き方では複数のstatement(日本語訳では文)になっているので書けません。
using()だからこう書く、と個別に覚えていくと無限の組み合わせになって覚えきれないと思います。
- using()の中ににはexpressionが書ける
- expressionとは何か、statementとは何か
を分けて理解することをお勧めします。
また、もし{ Href = "http://www.xbox.com/ja-JP/community/events/Home" }のなかに複数の文がある場合には? ;で区切るのでしょうか?
こちらはオブジェクト初期化子と言います。残念ながら都合よくF1ヘルプを呼び出すキーワードがありませんね。
ところで普通はF1キーがあるはずで、無いものといったらHHKぐらいでしょうか。ファンクションキーが必要とされるスキルレベルで、ファンクションキーの無いキーボードを選択されるのはどうかと思います…。
- 回答としてマーク d-kot 2011年2月6日 2:31
すべての返信
-
普通にMSDNに書かれているので読んでください。usingステートメント。
-
佐祐理さん、ありがとうございます。そうですか。
using(
はusingステートメントと呼ぶのですね!
読み方がわかれば、検索できる(気がします)。using (var sgml = new SgmlReader() { Href = "http://www.xbox.com/ja-JP/community/events/Home" }) {
xml = XDocument.Load(sgml);
}は
{
var sgml = new SgmlReader():
try {
Href = "http://www.xbox.com/ja-JP/community/events/Home"
}
finally
{
if (sgml != null) ((IDisposable)sgml).Dispose();
}
}
xml = XDocument.Load(sgml);ってことでしょうか?
でもこれって、finallyを実行すると、sgmlはDisposeされるから、nullになっちゃいます?Azuleanさん、コメントありがとうございます。
なぜかというと、このソースはコピーしてきたもので、書いてある内容がよくわからないのです。
わからなくても動けばそれはそれでよいのですが、いまあるhtml文書を解析しようとしていて、LINQ to XMLを使おうとしています。(次の質問はLINQ to XMLについてになります)。そのhtml文書は検索結果のURL(動的に変化する)から取得した動的に変化するhtml文書です。
それをこの構文に入れて使うためには、まあ、なにも考えずに、このURLのところに、
string url = Webから取得。
using (var sgml = new SgmlReader() { Href = url }) {
としちゃえばいいのですが、これから何度もテストしていくときに、毎回Webからとってくると、時間がかかります。毎回、20~30秒くらいかかるんじゃないかな。
で、テストでその時間を節約するためには、ここでURLを指定するのでなく、すでにローカルに取得してあるhtml文書を指定すればよいのではないか、と考えました。
で、それには、このURLの部分にファイル名を入れるか、(といまここまで書いていて思いついたのですが、ひょっとして、単にここに"file:///c:/savedfile.html"と書けばよかったのかも。やってみよう)、または、そのhtmlの内容はstringとしてもっているので、URLを指定するのでなく、htmlの中身を入れることはできないだろうか、と考えました。
htmlの中身は、File.Readしているのです。で、htmlの中身をいれるには、どこかで代入する必要があって、
最初は、
XDocument xml;
のところで、
XDocument xml = html;
と入れてみたのですが、型が違うとかなんとかでエラーになって、まあXDocumentという構造体(?)に、stringをそのまま入れるのは無理か~、それじゃもういちど読むか、で、読むには、URLの部分にファイル名を入れる必要があるけど、このHrefってなんだろう、だいたい、このusing(が意味がわからないので、書き換え方がわからないな~、だいたい()が複雑で読めないし。
そういえば、これまでも何度かこの構文は見たことがなくもないけど、自分で書いたことは一度もない。
まあそれじゃ、using(の書き方、using(を使わない書き方を両方を比較できるかたちで教えてもらえば、次からは自分で書けるようになるかも。
よし、訊いてみましょう!
という流れです。で、うえのように書いたのですが、これだと問題あり、ですよね?
コンパイルして動かして値をいろいろ入れてみれば、ひとりでも解決できるのですが、using(は読めないし、(ゆーじんぐかっこでは検索したとしても(と思って検索しなかったことがばればれですが、いまusing(で検索したら、
http://www.divakk.co.jp/aoyagi/csharp_tips_using.html
という、たいへん役立つトピックを発見しました。
なるほど。これはごきげんかもしれません。
一度も使ったことがないのはたいへんまずかったような気がしてきています。で、あらためて、
これを、using(を使わずに書くと、どうなりますか? -
using(
はusingステートメントと呼ぶのですね!
読み方がわかれば、検索できる(気がします)。Visual Studioをお使いならカーソルを合わせて F1 キーを押すだけですが。
でもこれって、finallyを実行すると、sgmlはDisposeされるから、nullになっちゃいます?
それ以前の問題として、変数のスコープの概念を理解できていないようです。usingとは別次元で、スコープを抜けると変数にアクセスできなくなります。
void SomeMethod(){
int i;
}
void OtherMethod(){
SomeMethod();
...;
// ここでSomeMethod()の変数 i にはアクセスできない
}このことはわかりますか?
これを、using(を使わずに書くと、どうなりますか?
この質問そのものにはd-kotさんがご自身で書かれているではありませんか。
何を聞こうとされているのかご自身で理解されていますか? だらだら書かれている文章と読むと、やっぱりusingとは関係なくて、SgmlReaderにローカルファイルを読み込ませる方法、つまりSgmlReaderクラスの使い方が知りたいだけなのではありませんか?
そういう空気を察したためにAzuleanさんは質問の意図を尋ねたのだと思います。
-
using (var sgml = new SgmlReader() { Href = "http://www.xbox.com/ja-JP/community/events/Home" }) {
xml = XDocument.Load(sgml);
}
は
(略)
ってことでしょうか?違います。
var sgml = new SgmlReader() { Href = "url" };
は、
var sgml = new SgmlReader();
sgml.Href = "url";と同義です。
こうして得られた sgml を try-finally の中で XDocument.Load とするわけです。でもこれって、finallyを実行すると、sgmlはDisposeされるから、nullになっちゃいます?
sgml は Dispose されますが、null になるわけではありません。
が、Dispose されているので使えなくなっています。(何か呼び出すと例外が出るかもしれません)using はその { } で囲まれた範囲でのみ使うオブジェクトを利用するときに楽をするための構文です。
using の行で作ったオブジェクトは、その { } 内の処理でのみ使うことができ、それ以降は使わない(=いらない)ので後始末することになります。
ですので、using を使う場合はその { } 内(ブロック内)に処理を書いてください。
もしくは、自分で Dispose する場所をきちんと管理し、Dispose 漏れが起きないように作り上げてください。このあたりの理解がない状態で進めることはおすすめできかねます。
一度、Dispose とはどういった仕事をするものか、今一度学んでください。XDocumentという構造体(?)
クラスです。
標準のクラスぐらいは調べてください。
http://msdn.microsoft.com/ja-jp/library/system.xml.linq.xdocument.aspx一般的に型が違う場合は代入できません。
代入できるのは明示的、あるいは暗黙的にキャストが可能として実装されている場合か、型変換の関数が用意されている場合に限られます。
そういった実装がない場合は、代入できないと考えるべきです。今回のご投稿を読む限り、初歩的なところをすっ飛ばしていろいろとチャレンジしようとしているように見受けられます。
チャレンジされることは良いと思いますが、そのコードが何を意味しているのか、なぜそういったことをしなければならないのかを捉える機会を持ってください。
あまり深く考えずに突き進んでいくと、プログラムがある程度動くかもしれませんが、メモリを使いすぎて動かなくなる、例外が出るといった手のつけられない事態に陥るかもしれません。
(そこまで大きくなってからだと、第三者には解決不可能なので、自分でがんばるしかありませんが)
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。 -
> 毎回Webからとってくると、時間がかかります。毎回、20~30秒くらいかかるんじゃないかな。
> で、テストでその時間を節約するためには、ここでURLを指定するのでなく、すでにローカルに取得してある
> html文書を指定すればよいのではないか、と考えました。こういうことは、よくやります。特に Web ページを読み取ってきて解析するようなツールでは、どの段階でどんな想定外のデータになっているか確認するためにも、定期的にファイルに保存しておくのがベターですね。
使っているクラス...この場合は、SgmlReader や XDocument のドキュメントぐらいは最低限読みましょう。あとは、処理を分割してできそうなことは自分でやるようにすること、ですね。using ステートメントを使うかどうかは、あまり関係がないように思います。
私は Tidy ばかりで SgmlReader を利用したことはないですが、URI を直接指定する以外にも、SgmlReader に要素を与える方法はいくつもあることはドキュメントを見ればすぐにわかりますよね?ですので、「HTML の取得処理」を SgmlReader の外側に出してやればよいだけではないかと思います。
よくやるパターンは、以下のようなかんじでしょうか。(これは、いまこの場で書いたものなので、きちんと動かないかもしれません) Debug が有効な場合には、ファイルに保存しておいた内容を優先的に使用します。
{ using (var stream = GetPage(new Uri("http://..."))) using (var reader = new SgmlReader(stream)) { var xml = XDocument.Load(reader); } } private static Stream GetPage(Uri uri) { #if DEBUG // URL からファイル名を作成 string filename = (new string[] { @"C:\temp\ramdisk\cache\web\", uri.Host, "\\", Regex.Replace(uri.PathAndQuery, @"[/\\:;\?]", "_"), ".html" }).Aggregate(Path.Combine); // ファイルが存在したら、そのファイルの中身を返す if (File.Exists(filename)) return File.OpenRead(filename); #endif // 指定された Uri の中身を取得する var req = WebRequest.Create(uri); using (var res = req.GetResponse()) { var stream = res.GetResponseStream(); var copy = new MemoryStream(); stream.WriteTo(copy); #if DEBUG // ファイルに保存しておく Directory.CreateDirectory(Path.GetDirectoryName(filename)); using (var file = File.Create(filename)) copy.WriteTo(file); copy.Position = 0L; #endif return copy; } } }
-
SgmlReader に Stream をもらうコンストラクタはなさそうなので、new SgmlReader { InputStream = stream } というように初期化子につけることでしょうか。
判断根拠: https://svn.mindtouch.com/source/public/sgmlreader/1.8.7/sgmlreaderdll/SgmlReader.cs を流し読み。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。 -
佐祐理さん、コメントありがとうございます。
だらだら思ったことを書くのもなんだから、最初には、簡潔に質問だけ書きました。
なぜかと理由を聞かれたので、思っていることを略さず書きました。VisualStudioは使っていますが、キーボードにF1キーがないので、押すことができません。申しわけありませんが。
変数のスコープはわかります。なんどもそれははまったので。
自分で聞きたいことはわかっているつもりです。
SgmlReaderクラスの使い方は知りたいですが、それは自分で試せばわかるかもしれないし、わからないかもしれません。まだ動いていないので、そんな先のことはわかりません。もしやってみてわからなければ、それはそのときの話です。
1行ずつ進んでいるだけですし。あと、usingステートメントについては、なにしろ今日初めてのことなので、これも理解できませんが、何度か構文じたいは使っているし、おもしろそうなので理解したいとも思っています。
Azuleanさんの質問の意図はよくわかりませんが、(わたしは特に質問がうまくないので)察していただけるのは大感謝ですが、いまのところ、SgmlReaderの使い方は(まだ)知りたいとは思っていないです。
もちろん、ごく直近に、次の段階はそこで躓く、ということは容易に予想できるかもしれないですけれども、いやでも、べつのことをしていてべつのことで躓いたので、その予想は予想としては外れですね。いまのところ、SgmlReaderにいつたどりつけるのかは、わたし自身にも皆目見当がつきません。
まただらだらになってしまいましたけれども。すみません。 -
Azuleanさん、コメントありがとうございます。
using (var sgml = new SgmlReader() { Href = "http://www.xbox.com/ja-JP/community/events/Home" }) {
xml = XDocument.Load(sgml);
}は
{
var sgml = new SgmlReader():
try {
Href = "http://www.xbox.com/ja-JP/community/events/Home"
}
finally
{
if (sgml != null) ((IDisposable)sgml).Dispose();
}
xml = XDocument.Load(sgml);
}//カッコの位置を間違えてました。ではなく、
{
var sgml = new SgmlReader():
try {
Href = "http://www.xbox.com/ja-JP/community/events/Home"
xml = XDocument.Load(sgml);
}
finally
{
if (sgml != null) ((IDisposable)sgml).Dispose();
}
}
ということですよね?
あ。違うか。
{
var sgml = new SgmlReader():
Href = "http://www.xbox.com/ja-JP/community/events/Home"
try {
xml = XDocument.Load(sgml);
}
finally
{
if (sgml != null) ((IDisposable)sgml).Dispose();
}
}
ってことですか。で、そうだとしたら、それが書式だといわれればそのとおりなのですが、
using (var sgml = new SgmlReader() { Href = "http://www.xbox.com/ja-JP/community/events/Home" }) {
xml = XDocument.Load(sgml);
}using (var sgml = new SgmlReader();
Href = "http://www.xbox.com/ja-JP/community/events/Home" ;
){
xml = XDocument.Load(sgml);
}
とかと書かないんでしょう?また、もし{ Href = "http://www.xbox.com/ja-JP/community/events/Home" }のなかに複数の文がある場合には? ;で区切るのでしょうか?
-
SgmlReaderを読み解く前に言語構文が理解できていなかったわけですね。誤解していました。
正式に、忠実に書くなら、
{
var _sgml = new SgmlReader();
_sgml.Href = "http://www.xbox.com/ja-JP/community/events/Home";
var sgml = _sgml;
try{
xml = XDocument.Load(sgml);
}
finally{
if( _sgml != null )
((IDisposable)_sgml).Dispose();
}
}かな。ただし、_sgmlは仮の変数でソースコードからは参照できません。なぜこうなっているかというと、try{}の中でsgml変数を書き換えても、元の値に対してDispose()を呼ぶためです。
で、そうだとしたら、それが書式だといわれればそのとおりなのですが、
using (var sgml = new SgmlReader() { Href = "http://www.xbox.com/ja-JP/community/events/Home" }) {
xml = XDocument.Load(sgml);
}using (var sgml = new SgmlReader();
Href = "http://www.xbox.com/ja-JP/community/events/Home" ;
){
xml = XDocument.Load(sgml);
}
とかと書かないんでしょう?using()の中はif()やfor( ; ; )の中などと同じくexpression(日本語訳では式)しか書けません。d-kotさんの書き方では複数のstatement(日本語訳では文)になっているので書けません。
using()だからこう書く、と個別に覚えていくと無限の組み合わせになって覚えきれないと思います。
- using()の中ににはexpressionが書ける
- expressionとは何か、statementとは何か
を分けて理解することをお勧めします。
また、もし{ Href = "http://www.xbox.com/ja-JP/community/events/Home" }のなかに複数の文がある場合には? ;で区切るのでしょうか?
こちらはオブジェクト初期化子と言います。残念ながら都合よくF1ヘルプを呼び出すキーワードがありませんね。
ところで普通はF1キーがあるはずで、無いものといったらHHKぐらいでしょうか。ファンクションキーが必要とされるスキルレベルで、ファンクションキーの無いキーボードを選択されるのはどうかと思います…。
- 回答としてマーク d-kot 2011年2月6日 2:31
-
佐祐理さん、ありがとうございます。
はい。ほんとうにusingステートメントの構文を理解できなくて、質問は単純にそれについてでした。もっとも、その後LINQやって、もういちどusingステートメントに戻ってきて、SgmlReaderに値を入れたら動かなくて、「ほらやっぱりいったとおりだろう」といわれれば、もちろんそのとおりではあります。
なぜその構文、すなわちSgmlReaderがusingステートメントの中に入った文をもってきたのかというと、そもそも構文がわからないのに下手に質問用に一般化して聞こうとして、一般化に失敗して質問したつもりで質問の体をなしていなかった、ということも経験しているので、できる限りそのまま抜いてくるほうがベターだ、という判断です。
using()の()のなかには、ifやforと同じく式(expression)を書ける。おお~。なんかわかった気がします。ifなら何度も書いたし。
これはわかりやすい。大感謝。
というように、いちいち考えていたことをたどっていくことは、わたしにとっては楽しいですけれども、だから、なぜ? と聞かれれば、それを順番に順を追って説明することはできるのですが、たぶん、これを読んでもだらだら書くな、といわれるのかな、という気はします。
そういうわけで質問のときには、かなり省略していて、変な意図推定されるようですが、ほんとうに単純にそれがわからない、ピンポイントでわからない、けっこう資料は見て、デッドロック状態で、最後の最後に(最後の最後が多すぎるということはさておき)、聞いています。HHKではないですが、間違いなくF1はありません。特別なキーボードですが、わたしは一生ぶんもっていて、それ以外のキーボードは使っていません。残念ながら(わたしにとっては別に困りませんが、いや困っているのか?)それが所与で、C#を使い始めたのはずっと最近のことであり、まさか開発環境がファンクションキーを必要とするだなんて思ってもみなかった、というのが印象です。QWERTYキーボードをタイピングできないので、選択肢がきわめて少ないのです。F5だかF6だかでデバッグ開始する機能もあるらしいのですが、当然ながら使ったことがありません。キーの割り当てがもっと自由だといいのですが、既存のソフトでは望むべくもなく、それはソフトを作るモチベーションのひとつです。
なぜC#をやっているかというと、先週だったかその前だったか、Azuleanさんにも問われて問われるままに答えましたが、その環境つまり、キーボード(ドライバ)、エディタ、ビューアー、データベース、インターフェースのすべてをC#で書いて置き換えれば移行できる可能性があるわけで、それをやっているところだからです。以下、雑談なので50行ほど省略。 -
別のスレッドで、shellもしくはWindow Managerを置き換えたいような発言をされていたので、このようなところで躓いているとは予想できませんでした。
もし文章を読まれることに抵抗がなければ、C# 言語仕様("C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC#\Specifications\1041\CSharp Language Specification.doc")を読まれるといいでしょう。ここに全てが書かれています。7.式、7.6.10.2.オブジェクト初期化子、8.ステートメント、8.13.usingステートメント、といった感じに。BNF記法でどこにどのような構文で書けるかも書かれています。
一気にすべてを理解する必要はないでしょう。分かるところから読んでいき、知りたい構文をかいつまんで読めばいいかと。
-
はい。シェルおよび、なんでもかんでもぜんぶを置き換えたいというのは、聞かれたから答えたまでで、わたしのプログラミング能力は相当低いです。
だいたいプログラマーではない、まあ遊びの部類だと思っています。たまたまサンデーでなく、フルタイムでプログラムを書いていますが、それを仕事にしているということもないので。
本/Webの文章を読むことには抵抗はないのですが、プログラミング関係だと、いちおうC#関係の本は5~6冊は読み、WPFの本も4冊くらい目を通して、随時参照していますが、ともかくひとつの文章中にわからない単語や概念が3つも4つも出てくる状況で、読んですんなり理解する、というレベルではとうていないと思います。
だから、あんまりそれで大それたことをいうと思われたかもしれないので、あんまりそういうなにをしたいのかとかいう質問をされると、答えちゃっていいんだろうかとしどろもどろですけれども。で、その
C:\Program Files\Microsoft Visual Studio 10.0\VC#
はありましたけど、
Specifications\1041\CSharp Language Specification.doc
は見当たらず、
CSharpProjects
Snippets
VCSPackages
が見つかっただけでした。ちなみにこの文章だと、
オブジェクト初期化子
ステートメント
BNF記法
をわたしは自分では説明できないですね。
自慢するわけではないですが…。頭が悪いとは思わないのですが、悪いのかな。ともかくプログラミングがそれほど得意でない、わからないことが山ほどあって、とりあえずわかっている範囲でなんとかしようとしている。わからないことも随時調べてはいるが、なかなかぴんとこない。
って感じですね。
ちなみに、この一連の処理ですが、ホームページのhtmlを解析して、Domツリーに入れるのはDOMツリーがまたややこしいので、えいやでLINQ to XMLするのに、SgmlReaderしてみようと思ったのですが、やってみたらSgmlReaderに通らないので、あきらめて、string処理で対応することにしました。
その程度の能力でそんな大それたことをいうのはバカかこいつはと思う方もいらっしゃるかと思いますが、別にそう思われることはいっこうに気にしませんが、着実に自分なりに成果は出しています。
いま、キーボードはソフトウェアキーボードをほぼ作り、デスクトップで常用する状態ではないですが、外出中に使える程度にはテストしました。海外旅行に、2度キーボードをもたずに出かけ、ソフトウェアキーボードのみを使う環境で試して、まあまあかなぁ、という程度。
エディタはむずかしくて何度も作っていますが、まだビューアーレベルです。もっともビューアーレベルといっても、WPFならテキストボックスをドラッグするだけで標準的な編集機能は実現できるので、編集までできなくはないですが。
画像ビューアーは、まあまあなんとか普通に使えそうな感じです。
データベースは中核においていて、毎日使っていて、1万行くらいは超えている規模ですが、毎日デバッグモードで動かしていてデバッグしながら使っています。
シェルインターフェースは、これも何度も作り直していますが、まだ満足いく機能を実装できていません。
あとメーラーも作りました。いまブラウザを作っています。
このすべてを毎日使っていて、ほぼ毎日、1個から3個くらい、調子がよいともうすこしずつ機能向上したり、バグフィックスしたりしています。
基本的に、既存のソフトは市販/フリー含めて、ほとんど使っていないです。ですから、自分のニーズを満たす分には、まあまあ足りていると思ってます。それで満足しているわけではないので、毎日苦戦してますけれども。