中年プログラマーの息抜き

ブログをはじめました。気の向くままにプログラム関連ネタをメモしていきます。

C#拡張メソッドでDataGridViewとEnumを密にする

はじめに

拡張メソッドを定義したいと思います。拡張メソッドというのは言語の型に機能(メソッド)を追加するような感じでしょうか。それでいてプログラミング言語で決まっているルールの範囲内というところが守られます。(難しいことは必要ない)

DataGridViewのCell

dataGridView1[columnIndex, rowIndex] でDataGridViewCellのオブジェクトが取得できますよね。しかし、DataGridViewを利用するときに列番号をEnumで管理したい。仕様変更に強い設計ですね。 そんなときこうなります。dataGridView1[(int)columnEnum, rowIndex]。このキャスト!数か所なら気にならないけど・・・なんかねえ。ということで

DataGridViewCellのための拡張メソッド

例えば下記のような拡張メソッドが容易に考え付きますが、これはコンパイルが通りません。Enum columnEnum を ValueType columnEnum とすれば通りますけど。。

public static DataGridViewCell Cell(this DataGridView self, Enum columnEnum, int rowIndex) {
     return self[(int)columnEnum, rowIndex];
}

Int32のソースコードでGetHashCode()をみてみる

referencesource.microsoft.comオブジェクトの値がそのまま返却されてます。

       public override int GetHashCode() {
            return m_value;
       }

EnumソースコードでGetHashCode()をみてみる

referencesource.microsoft.com

型付けされたクラスのGetHashCode()を呼び出してますね。

        public override unsafe int GetHashCode()
        {
            // Avoid boxing by inlining GetValue()
            // return GetValue().GetHashCode();
 
            fixed (void* pValue = &JitHelpers.GetPinningHelper(this).m_data)
            {
                switch (InternalGetCorElementType())
                {
                    case CorElementType.I1:
                        return (*(sbyte*)pValue).GetHashCode();
                    case CorElementType.U1:
                        return (*(byte*)pValue).GetHashCode();
                    case CorElementType.Boolean:
                        return (*(bool*)pValue).GetHashCode();
                    case CorElementType.I2:
                        return (*(short*)pValue).GetHashCode();
                    case CorElementType.U2:
                        return (*(ushort*)pValue).GetHashCode();
                    case CorElementType.Char:
                        return (*(char*)pValue).GetHashCode();
                    case CorElementType.I4:
                        return (*(int*)pValue).GetHashCode();
                    case CorElementType.U4:
                        return (*(uint*)pValue).GetHashCode();
                    case CorElementType.R4:
                        return (*(float*)pValue).GetHashCode();
                    case CorElementType.I8:
                        return (*(long*)pValue).GetHashCode();
                    case CorElementType.U8:
                        return (*(ulong*)pValue).GetHashCode();
                    case CorElementType.R8:
                        return (*(double*)pValue).GetHashCode();
                    case CorElementType.I:
                        return (*(IntPtr*)pValue).GetHashCode();
                    case CorElementType.U:
                        return (*(UIntPtr*)pValue).GetHashCode();
                    default:
                        Contract.Assert(false, "Invalid primitive type");
                        return 0;
                }
            }
        }

DataGridViewCellのための拡張メソッドはこうですね

EnumがInt32に型付けされていれば、下記のような拡張メソッドで実装が容易になるし、キャストの気持ち悪さから解放されます。。

public static DataGridViewCell Cell(this DataGridView self, Enum columnEnum, int rowIndex) {
     return self[columnEnum.GetHashCode(), rowIndex];
}

まとめ

拡張メソッドは重宝します。… ただ、個人的に単純なstaticな機能で提供されているもの(string.IsNullOrEmpty()など)をわざわざ拡張して実装しなおす必要なないと思っていて、例えば、今回のように実装しなおすとコードが読みやすくなると思えば利用するのはありなのかなと思います。 ほかにもstaticな機能が提供されるが複数の決まりきった引数を省略するためにこんな拡張もあり?

 

public static T ToEnum<T>(this Int32 self) {
     return (T)Enum.ToObject(typeof(T), self);
}


public static string ToName(this Enum self) {
     return Enum.GetName(self.GetType(), self);
}

 

db39

CISCO 841M 設定:その13:L3スイッチでVLAN 間のルーティングをフィルタする

f:id:tm-b:20170728004131j:plain

はじめに

L3スイッチが4ポートあり、それぞれVLAN1, VLAN2, VLAN3, VLAN4で分離したとしても、ルーティング機能により、VLAN2⇔VLAN3などVLANをまたいだ通信が可能です。(ノードからping を打つと届きます。)今回は例として、VLAN2⇔VLAN3をフィルタし通信を出来なくなるようなACLを考えてみました。(ノードからping が届かなくなります。)

VLAN2は「192.168.20.0/24」、VLAN3は「192.168.30.0/24」とします。

機材

CISCO 841M本体
・KAUMO USB RJ45 シリアルコンソールケーブル
・Win10 ノートPC

VLAN内でのフィルタイメージ

・VLAN2のACLグループ(120)を作成、設定する

・VLAN3のACLグループ(130)を作成、設定する

ACLグループ(120)

no access-list 120
access-list 120 deny ip any 192.168.30.0 0.0.0.255
access-list 120 permit ip any any
interface Vlan2
  ip access-group 120 in
  exit

ACLグループ(130)

no access-list 130
access-list 130 deny ip any 192.168.20.0 0.0.0.255
access-list 130 permit ip any any
interface Vlan3
  ip access-group 130 in
  exit

まとめ

in と out のイメージがつかみにくかったですが、VLAN内のACLということで考えをまとめ、整理しました。ネットワーク単位ではなくノード単位で細かく設定する場合は特にだと思いますが、いろいろな可能性を考えると、VLANで設定しておくほうが人にやさしいのかなと…

PERL[CGI]でPHPのSESSIONを利用してみます

はじめに

PERLで作ったダウンローダーって行数少なくソフト的にはファイルサイズに上限がないなど、個人的に便利なので社内のちょっとしたシステムでよく使います。

一方でメイン画面は他言語でPERLを使う機会は減っていて、ほかの言語で書いたプログラムとセッション情報を共有するには工夫が必要なこともあります。

今回は、メイン画面をPHPでログイン処理するようなシステムがあり、そのセッション情報をPERLから利用する機会があったのでそのメモです。

簡単に機能説明

PHP
ログイン情報をセッションで保持する
({ id }を含む連想配列をloginという名前で保持する)
PERLCGI
ログイン情報をPHPのセッションから取得する
(取得できればファイルをダウンロードさせる)
リクエストパラメータでファイルを特定する
(id=5 みたいなパラメータでMySQLを検索)

依存ライブラリ(CPAN

ライブラリ使ってみます。下の三つを配置してCGIスクリプトから参照。CPANの環境からだと「PHP::Session」を入れたら全部落ちてくるような気もします。

CGIDBIなど一般的なライブラリ
XSERVERなどレンサバで最初から使えるものは割愛します。
・UNIVERSAL::require
https://metacpan.org/pod/UNIVERSAL::require
PHP::Session
https://metacpan.org/pod/PHP::Session
PHP::Session::Serializer::PHP
https://metacpan.org/pod/PHP::Session::Serializer::PHP

CGIのサンプル

エラー処理は省略。(例えば、PHP::Session->new が失敗すると異常終了します)

#!/usr/bin/perl
use strict;
use warnings;
use lib 'modules';#依存ライブラリを配置したディレクトリ

use CGI qw(:standard);
use PHP::Session;
#セッションファイルの場所はphpinfo()等で確認して、
my $session = PHP::Session->new(cookie('PHPSESSID'), { save_path => '/***/php/session' });
if ($session && $session->get('login') && $session->get('login')->{'id'}) {
    use DBI;
    use Encode qw/encode_utf8/;
    use URI::Escape;

    my %form = ( "id" => "" );
    my $query = $ENV{'QUERY_STRING'};
    $query =~ tr/+/ /;
    $query =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2', $1)/eg;
    foreach (split(/&/, $query)) {
        my ($k, $v) = split(/=/,$_);
        $v =~ tr/+/ /;
        $v =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
        $form{$k} = $v;
    }

    if ($form{"id"}) {
        my $dsn = "dbi:mysql:database=***;host=***;port=***";
        my $user = "***";
        my $pass ="***";
        my $dbh = DBI->connect($dsn, $user, $pass, {
            AutoCommit => 1,
            PrintError => 0,
            RaiseError => 1,
            ShowErrorStatement => 1,
            AutoInactiveDestroy => 1
        })|| die $DBI::errstr;
        my $sth = $dbh->prepare("SELECT name `ファイル名`,path `ファイルパス` FROM ***);

        $sth->execute();
        if (my @row = $sth->fetchrow) {
            my $name = URI::Escape::uri_escape($row[0]);
            open IN,"$row[1]" || die "E3001:400 Bad Request.";
            print "Content-type: application/octet-stream\n";
            print "Content-Disposition: attachment; filename=\"$row[0]\"; filename*=UTF-8''$name\n\n";
            print ;
            close IN;
        } else {
            die "E3002:400 Bad Request.";
        }
        $sth->finish;
        $dbh->disconnect;
    } else {
        die "E3003:400 Bad Request.";
    }
} else {
    die "E3004:401 Unauthorized.";
}



1;

まとめ

PERLは好きな言語です。

ab39