タイトル | : Re^9: .netでのストリームの扱い |
記事No | : 11493 |
投稿日 | : 2015/09/02(Wed) 21:00 |
投稿者 | : 魔界の仮面弁士 |
> Sub Write( buffer() As Byte, offset As Integer, count As Integer )
具体的には、System.IO.Stream クラスが持つ下記のメソッドです。 https://msdn.microsoft.com/ja-jp/library/system.io.stream.write.aspx
Stream は、MemoryStream や FileStream の継承元となる抽象クラスです。 そして Stream から派生された全てのクラスが、上記の Write メソッドを装備しています。 (ちなみに Stream 派生クラスは、殆んどの場合「何某Stream」というクラス名が付けられています)
まぁ、読み取り専用(.CanWrite = False)なストリームだと使えませんけれどね。 そのようなストリームで Write を呼ぶと、実行時に例外が発生するはず。
> streamwriteにWrite( buffer() As Byte, offset As Integer, count As Integer )を実行したところ > [これらの引数で呼び出される、アクセス可能な 'Write' がないため、オーバーロードの解決に失敗しました] > となりました StreamWriter はストリームではありませんよ。何某Stream という名前では無いですよね。 StreamWriter の継承元は System.IO.Stream ではなく、System.IO.TextWriter です。
StreamWriter の Write メソッドにおいて、3 個の引数を受け取るオーバーロードは .Write(Char(), Int32, Int32) .Write(String, Object, Object) の 2 つしかありません。そして「Byte(), Int32, Int32」はどちらにも解決できないため、 コンパイルエラーになってしまうというわけです。
Stream.Write( Byte(), Int32, Int32 ) を、No11491 の例で呼び出すとすれば ms.Write(raw, 0, raw.Length) txtWriter.BaseStream.Write(raw, 0, raw.Length) binWriter.BaseStream.Write(raw, 0, raw.Length) などといったコードを記述できます。 とはいえ今回は、BinaryWriter の Write( Byte() ) を使った方が手っ取り早いでしょうね。
> (2)また、 > あるいは StreamWriter や BinaryWriter の類は一切使わずに、、、 > こちらについても、もう少しヒントをいただけないかと、、
ストリーム(Stream継承クラス)への書き込みに、 ライター(BinaryWriter、StreamWriter 等)を使わないということです。
ファイルの内容をバイナリとして取得したいなら、 raw = File.ReadAllBytes(ファイルパス) 文字列を Shift_JIS バイナリとして取得したいなら、 raw = Encoding.GetEncoding("Shift_JIS").GetBytes(文字列) として取得できますので、あとは Byte配列である 上記の raw を 今回の ms As StreamWriter に対して ms.Write(raw, 0, raw.Length) の構文で書き込むということです。
> .netになってから変数宣言をブロックの頭にしなくてもよくなったのでしょうか もっと前からです。VB6 においても、下記のコードは動作しますよ。
Option Explicit
Private Sub Form_Load() Dim a As Integer a = 123 's = "Before" 'これは NG Dim s As String s = "After" 'これは OK End Sub
ちなみに VBScript に至っては下記さえも OK ですが…これは特殊な例ですね。
Option Explicit Dim a a = 123 s = "TEST" Dim s MsgBox s
> わかりますが、変数の利用状況がわからないから行頭に書けと > ずいぶーーん以前から頭に入ってます。。 旧 BASIC 言語や VB2 の頃は、そう言われていましたね。 現在の私は、「使う直前に宣言 & 初期化」派ですけれども。
初期化が容易になったというのも、変化が訪れた理由の一つですが、 他にも理由については諸説あります。たとえば:
・ANSI C (≠C++)では、ローカル変数の宣言を関数などのブロックの先頭で宣言しなければいけなかった。 当時は他にも、先頭に用意された「変数宣言のためのセクション」の中でしか変数を確保できない言語が 多かったため、それらとの移植性を容易にするという事情があった。
・メモリ等が今ほど潤沢でなかった当時は、無駄な未使用変数を確保すべきではなかったため、 管理しやすいよう、冒頭に記述することが推奨されていた。
などが思い当たります。
ところが冒頭で宣言する方法の場合、コードが長くなってくるにつれて、 「変数を利用している箇所」と「変数の宣言箇所」とが遠く離れてしまうため、 変数の数やコード量が増加するに伴って可読性が低下してしまいます。 (長すぎるコードというのは、それはそれで別の問題があるわけですが)
それに最近のコンパイラなら、未使用変数は自動的に検出してくれますので、 冒頭に書かなければならない理由は薄れてしまいました。このような理由から 特に VB.NET においては、使う直前で宣言する手法も増加してきています。
ただ、冒頭に宣言する手法が必ずしも悪いという訳ではありませんし、このあたりは好き嫌いの問題でもあります。 そもそも VB6 の場合は未使用変数のチェック機構などがありませんしね。
なお、ローカル変数の扱いについては、VB.NET になってからも微妙に変化しています。
2002 の頃は Dim i As Integer For i = 1 To 10 Console.WriteLine(i) Next と書かねばならなかったコードが、2003 からは For i As Integer = 1 To 10 Console.WriteLine(i) Next と書けるようになりました。
前者だと、For ループを抜けた後も変数 i を利用できてしまいますが、 後者なら、その For 内でしか i を使えないため、変数のスコープを より狭くすることができます。
さらに 2008 になってからは、「型推論」のおかげで、As Integer すら省略して、 For i = 1 To 10 と書けるようにもなっていますね。
そのほかの VB6 との違いとしては、こんなのもあります。 Dim i As Integer For i = 1 To 10 Dim x As Integer x = x + i Next Call MsgBox(x)
変数 x は For ブロック内で宣言されているため、 VB.NET の場合、このブロックの外ではアクセスできません。 しかし VB6 はブロックレベル変数を考慮しないため、 上記のコードでも動作するという違いがあります。
とはいえ従来の VB においても、「カプセル化」を意識した開発者であれば、 「グローバル変数は無闇やたらと使わない」 「Public なプロシージャは避け、極力、Private を使う」 といった考え方を持っていました。ブロック変数が使えないという言語上の制約はあれども、 変数のスコープを狭く抑えるべきだという流れは、今も昔も同じかと思います。 (宣言箇所をできるだけ「使う直前」に行うのも、スコープを狭める一助となります)
その一方で、たとえ利用箇所が後の方であっても、あえて先頭に書いた方が分かりやすいこともあります。 具体例を挙げるのは難しいのですが、たとえば メソッド内で重要な意味を持つ変数(戻り値を保持する変数など)や、 メソッド内での使用頻度が非常に高い変数などの場合などですかね。
また、関連性のある変数群は、それらを一箇所にまとめておいた方が わかりやすいという考え方もあるでしょう。それが先頭部であろうとなかろうと。
――ですから、何が何でも「利用する直前」に宣言するべきだと、というわけでもありません。加減は必要ですね。
> (こんなこと検索しても出てきません(笑)) VB4 の Books Online に記載されていたような記憶もありますが、超うろ覚えです。 当時は Form1!Text1 派と Form1.Text1 派がいたりもしたっけか…。
|