C# で Java と同等のアクセス制限をかける

Java プログラマが C# を学ぶ際のメモその2。
 
– Java
public: どこからでもアクセス可
protected: 自クラス内と子孫クラス、または同一パッケージ内であればアクセス可(default 権限も兼用)。
default: 同一パッケージ内からのみアクセス可。アクセス修飾子を省略した場合のデフォルト。
private: 自クラス内からのみアクセス可。
 
– C#
public: どこからでもアクセス可(入れ子 enum,interface のデフォルト)。
protected internal: 同一アセンブリ(DLL ファイル, EXE ファイル等)内と子孫からアクセス可。
internal: 同一アセンブリ内からのみアクセス可(名前空間直下で宣言したコンテナのデフォルト)。
protected: 自コンテナ(クラス等)内と子孫からのみアクセス可。(Java と違い同一名前空間は不可)
private: 自コンテナのみからアクセス可(入れ子 class, struct のデフォルト)。
 
注意すべきは、protected の動作の違いと、default(Java) と internal(C#) の制限範囲の違いです。
Java では、パッケージレベルでの制限が可能ですが、言語で制限をかけただけだと外部ファイルからのパッケージへの侵入が可能です。
これを防ぐには JAR を作る時にシーリング機構を利用すれば、そのパッケージの利用者の侵入を制限することができます。
マニフェストの不正な書き換えを防ぐにはJARへの署名機能等を使えばよさそうです。
 
C# では、アセンブリ(プログラム, ライブラリ)単位でのアクセス制限が可能で、名前空間単位では制限できません。アクセス制限がソースファイル単位ではないことに注意してください。
参考:
C# プログラマーズリファレンス – アクセシビリティレベル
JARマニフェストのメイン属性
JavaHouse Brewers 19189 – Package Joining Attack(JDK1.2 での話)

パケットキャプチャツール

昔(2年ほど前)使っていて、機能的にも優秀で使いやすかったものなのですが、
名前を度忘れしていて今日やっと思い出しました orz.
 
Wireshark
(Etherealから改称)
ネットワークに実際に流れている中身をキャプチャして、GUI で見ることができるオープンソースなパケットキャプチャツールです。
Ethereal, Wireshark の Windows での動作には WinPcap が必要です。
 
Windows の他に、Linux 等さまざまな環境で動作します。
 
追記:
(2006-07-27) Ethereal は Wireshark として更新されるようになりました
参考:
Ethereal(イーサリアル)の日本語解説サイト
Wireshark

sh での変数とワイルドカードの落とし穴

sh, bash, csh, zshのようなシェルでは

echo *.sh

などというように、ワイルドカードでファイル一覧を取得することができます。
シェルで使えるワイルドカードはアスタリスク(*)、クエスチョンマーク(?)の2種類で、それぞれ、0文字以上の任意の文字列、1文字の任意の文字(英数記号など)を表します。

ワイルドカード文字を、ワイルドカードとしてではなくただの * や ? という1つの文字として扱いたい場合は、次のようにクオート記号で囲むか、エスケープします。

echo "*.sh"
echo "What's up?"

# 結果
# *.sh
# What's up? 
# が表示される

ここに変数が関係してくると、少し厄介なことになります。

# 間違った例
foo="SELECT * FROM table"
echo $foo

この結果は、

SELECT * FROM table

と表示されるかと思いきや、アスタリスクがワイルドカードとして展開されてしまうため、

SELECT FROM table

とアスタリスクが消える、または、現在の作業フォルダのファイル一覧が展開されて、

SELECT bin boot home root var FROM table

のように意図しない結果になってしまいます。

これはかなり怖いことで、この動きにより、最悪致命的なセキュリティホールや、データ消失につながる場合があります。

これを回避するためには, ワイルドカード文字を含む変数の参照時は常にダブルクオート/シングルクオートの中で参照するというテクニックを使います。

# 正しい例
foo="SELECT * FROM table"
echo "$foo"

この結果は、期待通り

SELECT * FROM table

となります。

多段になってもこの動きは変わりません。

foo="SELECT * FROM table"
boo="$foo"
echo "$boo"
# foo, boo 両方の参照をダブルクォートでくくっていることに注意

この結果も、期待通り

SELECT * FROM table

となります。ここで

echo $boo

としていると、やはり変数展開されてしまうため、注意しましょう。
お約束として、変数を参照するときで、変数に記号が入る可能性がある場合は必ずダブルクオートで囲む癖をつけると、こういう間違いがなくなるので幸せになれます。

おまけ

一歩踏み込んだ例として、変数の遅延展開の例を紹介します。

export table=book # 1
# ↑これは全く利用されない
text='SELECT * FROM ${table}' # 2
# ↑シングルクオートなので、textに入る時点ではtableが展開されない。

export table=member # 3
# ↑これが利用される。子プロセスで参照できるよう export している
query=$(sh -c "echo \"$text\"") #4
# ↑ここでtableの中身が展開されます。* の展開を防ぐため $text をダブルクォートで囲んでいることに注意
echo "$query" # 5

この結果は、

SELECT * FROM member

と出力されます。
処理の流れは以下のとおりです。

1: table 変数に book をセット
2: text 変数に SELECT * FROM ${table} をセット(table変数の中身はここでは見ない)
3: table 変数の内容を member に書き換え
4:

sh -c "echo \"$text\""

を実行、shが立ち上がり、

echo "SELECT * FROM ${table}"

を実行する。(ここでtableが初めて展開される)
query 変数に、echo が出力した SELECT * FROM member をセットする。(ここでは変数代入だけで画面には出力されない)
5: query 変数の内容、SELECT * FROM member を表示する

あまりやらないですが、こういう複雑な処理でも期待通り動作するのは、適切にクオートしているおかげですね。

SQLite でパフォーマンスチューニング – インデックス

クエリを実行して、インデックスの利用状況を調べるには、公式サイトからダウンロードできる idxchk を使います。

tclsqlite3 idxchk db_filename ‘SELECT … ‘

結果は次のような感じになります。

 table index(es) column(s)
 —— ———- ——————————
 foo (none)
 boo <pk> <integer primary key or ro>
 
 SQLite version: 3.2.8

この場合 boo テーブルの Primary Key がインデックスのように作用していて、boo, foo にはインデックスがない、あるいはこのクエリに影響を与えるインデックスがない、ということです。
参考:
IdxChk英語のオンラインマニュアル

SQLite でパフォーマンスチューニング – クエリのベンチマーク

パフォーマンスチューニングにおいて、クエリの実行時間を調べるのは基本ですが、
SQLite では SQL 文だけでは、かかった時間を調べることができなさそうなのでたとえば次のようにします。

time sqlite db_filename “SELECT …” > /dev/null

結果はこんな感じになります。

real 0m0.239s
user 0m0.050s
sys 0m0.060s

time は AT&T UNIX 系標準のコマンドで、引数に渡したプログラムの実行時間を出力します。
 
もちろん、SQLite にかぎらず、どんなプログラムに対しても実行可能です。
 
参照:
MANPAGE of TIME

SQLite3.x で COPY を使うとエラーになる

SQLite3.x で

COPY footable from ‘foo.csv’ USING DELIMITERS ‘,’;

などとして csv から読み込もうとすると、

SQL error: near “COPY”: syntax error

となり実行できません。
 
調べてみると、3.0 以降削除された機能だそうで、公式のバグトラックによると

3.0 以降、不自由ですが COPY コマンドは削除しました。
このコマンドは SQL 非標準であり、UTF-16 での実装方法についてどうすればいいか見出せなかったためです。

とのことでした。
 
2.x と 3.x を両方いれて、2.x で csv からコピーしてdumpしてやることで 3.x でも CSV の利用は可能です。

$ sqlite /tmp/foocsv.tmp “COPY foo FROM ‘foo.csv’ USING DELIMITERS ‘,'”
$ sqlite /tmp/foocsv.tmp ‘.dump’ > /tmp/foocsv.sql
$ sqlite3 ./applyTo.db < /tmp/foocsv.sql

 
参考:
Ticket 982: COPY command does not work in 3.0.8 as it does in 2.8.13
SQLite が認識できるクエリ言語

Allowed memory size of 8388608 bytes exhausted

portupgrade 利用中に以下のエラーに遭遇。

# portinstall devel/pear
… (中略) ..
Fatal error: Allowed memory size of 8388608 bytes exhausted (tried to allocate 56 bytes) in /tmp/gopeypYZxF/PEAR/Registry.php on line 1006
Allowed memory size of 8388608 bytes exhausted (tried to allocate 5 bytes)
 *** Error code 1
 
Stop in /usr/ports/devel/pear.
 *** Error code 1
… (以下略)..

portinstall 中のことで一瞬何かと思いましたが、PHP 周りで 8388608bytes = 8M 制限といえば、php.ini の次の設定ですね。

memory_limit = 8M ; Maximum amount of memory a script may consume (8MB)

これを、一時的に 16M とかしたら問題なくインストールできました。

C#.NET と MVC(PAC)

件名:PACパターンにおいて、Formクラスはコントロール層なのか?
自動生成の Form だと、MVC の V+C な動作をしていますね。
結論としては、ここの流れどおり、コントローラを別に作って、そっちをエントリポイントにするのがベターじゃないかなと思います。
 
2006-02-08 追記:
よく使われる Application.Run(Form) だと、せっかく Main() メソッドを分離してもイベントハンドラの段階で V+C な形になってしまうので、ApplicationContext インターフェイスを実装してこれを Application.Run(ApplicationContext) に渡してやると、コントローラとビューの分離がよりスマートにできる。
 
2007-04-10 追記:
ApplicationContext を利用した場合の実装例はこんな感じになります。

using System;
using System.Windows.Forms;
 
class Controller : ApplicationContext{
    // MVC の view レイヤ( Controller との接合部のみを IView として interface にしてもよい)
    private Form1 view;
 
    // コンストラクタ
    internal Controller(){
        view = new Form1();

// フォームが閉じられた時にアプリケーションを終了させるためのイベントハンドラを追加。
view.Dispose += new EventHandler( delegate (object sender, EventArgs e){
Dispose();
Application.Exit();
});
// フォームを表示
view.Show();
    }
}

あとはこれを Program.cs などエントリポイント( static void Main(string[]) 関数)の

Application.Run(new Form1);

などとなっている箇所を

Application.Run(new Controller());

のようにすれば、直接 Form を呼び出す実装ではなく ApplicationContext を介した実装になります。
 
参考:
.NET TIPS – VB.NETでアプリケーション起動時に実行されるフォームを変更するには?(@IT)
VS.NET でのエントリポイントの設定の仕方について書かれています。
Application.Runメソッド(ApplicationContext) (MSDN -> .NET Framework-> リファレンス -> クラスライブラリ -> System.Windows.Forms -> Application クラス -> メソッド -> Run メソッド)
リンク先のサンプルの動作には次の宣言が必要。また、MyForm1, MyForm2 というフォームクラスを用意する必要がある。

using System;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using System.ComponentModel;
using System.Text;