自動プロパティの暗黙具合に死亡

class Rozen
{
    public string Name
    {
        get;
        set;
    }
};

みたいな感じで、C#で自動プロパティ作った後に「setter で来る value をトレスしたいな!」なんて思い始めた際に積む感じですかねやはり…? 自動プロパティが生成する暗黙のフィールドあたりに value を代入できないですよね… 暗黙だし

class Rozen
{
    public string Name
    {
        get;
        set
        {
            Debug.Print("Rozen::Name={0}", value);
            ...??? = value; // 死ぬ死んだ
        }
    }
};

自前で値保存用のフィールド設けるしかないですよねぇ… プロパティの数増えてきたも、全部自前で「プロパティと対となるフィールド」を作らないと行けないですよね…。 setter 呼ばれた際に トレスじゃなくて event を callback する機能追加ーなんて時も同様ですよねぇ…

ですよねぇ…

ですよねぇ……

ですよねぇ………

あああ

クラス変数初期化時に他のクラスメンバが見つからなくて死亡

にわかpython屋のわし、詰まる。

#python3

class A :

    def set(self, type, value) :
        A.FUNC_MAP[type](self, value)
    
    def _setSearch(self, s) :
        self.search = s
    
    def _setTitle(self, s) :
        self.title = s

    FUNC_MAP = {
        "search" : A._setSearch,    # NameError: name 'A' is not defined
        "title"  : A._setTitle,
    }

a = A()
a.set("title", "たいとる")
a.set("search", "検索語")

print(a.title)
print(a.search)

A::set() でメンバ変数をセット、セットする先の変数も関数の引数の文字列で dispatch する…って事をやりたくて、キーワードに対する関数マップを作りました。 が、A::_setSearch が見つから無ぇ! なんだとメガトロン!?

…と、一睡した結果こうなりました。

#python3

class A :

    def set(self, type, value) :
        A.FUNC_MAP[type](self, value)
    
    def _setSearch(self, s) :
        self.search = s
    
    def _setTitle(self, s) :
        self.title = s

    FUNC_MAP = {
        "search" : _setSearch,  # クラス名修飾要らない
        "title"  : _setTitle,
    }

a = A()
a.set("title", "たいとる")
a.set("search", "検索語")

print(a.title)
print(a.search)

嘘だと言ってよバーニィ! クラス変数初期化時はクラス名が不要だったなんて……


class Kashira :

    clazz = 10
    
    def bar(self) :
        print(Kashira.tamagoyaki)

とか、メンバ関数内からであってもクラス変数参照する際はクラス名を修飾しないとアカンので、すっかり付ける事しか頭に無かったわ…

Nameエラーの内容が「"A" なんか知らねぇよ。糞して寝ろ。二度と来んな」って言ってるって事は、つまる所クラス変数初期化時にまだ class A が存在しないって事なんですね。 おそらく初期化中だから。 なるほど…



あ、この程度なら dict に lambda 直接突っ込めばいいじゃんというツッコミは無しでお願いします(ぉ

ironおっpythonをnunitと絡めると垂れ落ちて死亡(?)

NUnitのテストクラス中で、IronPythonを利用してニャンニャンしようかと思ったのですが、どうも落ちる。

NUnitIronPython は相性が悪い模様……? なお、NUnitGUI/Console でも、x86指定が有るヤツ/無いヤツ問わず全てで「署名がうんこです。糞して寝ろ」って落ちます。 何故や…いや確かに、お腹痛かったけどさ…

同じ環境下での解決方法は今のところ見つかっておりませぬ…。 4時間ぐらいほっといたコーヒーがアカンかったのだろうか…

バージョンとか

…色々大人の事情があるんですえぇ……

エラー

SetUp : System.IO.FileLoadException : ファイルまたはアセンブリ 'Microsoft.Scripting, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'、またはその依存関係の 1 つが読み込めませんでした。厳密な名前の署名を確認できませんでした。アセンブリが故意に変更されているか、または適切な秘密キーを使用した完全署名ではない、遅延署名がされています。 (HRESULT からの例外: 0x80131045)

コード

using System.Text;
using System.Net;
using System.Collections.Specialized;
using System.Collections.Generic;

using Microsoft.Scripting.Hosting;
using NUnit.Framework;

namespace A
{
  [TestFixture]
  class Programs
  {
    // コンソールアプリケーションあたりにして、単独で実行すると落ちない
    public static void Main(string[] args)
    {
      Programs p = new Programs();
      p.ClassSetup();
    }

    [TestFixtureSetUp]
    public void ClassSetup()
    {
      // ここで和式便器からうんこ漏らす
      ScriptEngine engine = IronPython.Hosting.Python.CreateEngine();
    }

    [SetUp]
    public void Setup()
    {

    }

    [TearDown]
    public void TearDown()
    {

    }

    [TestFixtureTearDown]
    public void ClassTearDown()
    {

    }

    [Test]
    public void Test00()
    {

    }
  };
};
プロジェクトのアセンブリ参照

dll 的には

問題無かった環境

では、問題有りませんでした。 「今更 2008 (しかもEE) 使ってるおめーの環境がうんこなんだよ」って、はいすいませんすいません…

【ぼやき】catchって抜けられないんスよねそういえばあああ

try
{
	funcNanika();
}
catch(Exception ex)
{
	if(ex is ObjectDisposedException)
		break;	// ココ。唯一の問題無い想定の例外なので正常系として処理。finally に飛ぶ
	Log(ex);
}
finally
{
	funcFinally();
}

とかできると嬉しいなぁってシーンに遭遇しました。

…いやまぁ ObjectDisposedException を別口で catch しろよって話なんですけどね!怠けてるんじゃねーよって話なんですけどね!!

DeploymentItem属性に踊らされて死亡

このお話の結論

[TestMethod]
[DeploymentItem(@"img\src.bmp")]
public void Test00()
{
	...
}

とか書いた場合 "img\src.bmp" がコピーされるタイミングは、テスト自体を実行した時である。 その関数が実行される直前にコピーではない! あくまでテスト自体を実行開始した時である。

TestInitialize とか ClassInitialize とか AssemblyInitialize とかでブレーク貼ると、止まった時には既にファイルがコピーされているのだ…!

というわけで、(Test|Class|Assembly)Initialize で何かのファイルに依存する場合は、テキトーな TestMethod に合わせて DeploymentItem を書いておけば良いかなと思います。 もしくは、[TestMethod] void ResourceInitializer() とか DenependResourceDeployer() とか、それっぽい名前をもちつつ中身空っぽのダミーのテスト関数作ってそこにモリモリ書いていくとか。

…えー…… ('A`)


クラスに DeploymentItem属性 つけられてファイルコピーもされました。

[TestClass]
[DeploymentItem(@"img\src.bmp")]
public class TestClass
{
    ....
}

…えぇぇぇ… ('A`)('A`)('A`)

ことの始まり

とあるクラスのメンバ関数をテストを行いたかったのですが、そのクラスはコンストラクタで Image が要求さていました。この Image はファイルから読み込んで渡してあげれば良いのですが、定数的に変化しない内容である為テスト毎にファイルを読み込むのもダサい(ぉ*1 クラスのインスタンスはテスト毎に作り直すのは良い*2としても、Imageについては一旦読み込んだらそれ使い回すべさーと思い立ったは良いのですが………


ClassInitialize で死亡

テストクラス中1回読めばよかったので ClassInitialize でファイルを読み込むようにしました。

[ClassInitialize]
[DeploymentItem(@"img\src.bmp")]
public static void ClassInitializer(TestContext context)
{
    _srcImage = Image.FromFile("src.bmp")
}

ファイルが無ぇってさ

DeploymentItemTestMethod でしか有効でないらしいです。 嘘だろ承太郎!? じゃぁ自分でファイル持ってくるかーとか考えるわけですが、この ClassInitializer の時点で詰んでる。

  • カレントディレクトリが TestResults の下。[c:\\フロニャルド\\TestResults\\Deploy_millhi 2015-09-29 11_19_39\\Out] とか。
  • ファイルコピー元のディレクトリを引っ張ってこれない。 テストプロジェクトのあるディレクトリとか、その下の [bin\Debug] とか取れない…!
  • context の中に期待する情報があると思った? 残念! TestResults 以下のモノしか無ぇよ!!

どうすんねん…

ってか、じゃぁテスト実行時に元テストプロジェクトのパスが取得できないと、TestMethodDeploymentItemさんは、どーやってファイルコピーしてきてるのよ。 どこから元プロジェクトのパス引っ張ってきてるのよ? UnitTesting名前空間以下によさ気なクラスがあるわけでもなさそうだし…まさかのpdbからとか? …TestResults 以下にんなもんコピーされてないよ? 上上下下左右左右BA 押してもダメだったよ? ってか俺R右下Y下右下左右下右右AA派だよ?*3 ソレがダメすかもしや…!?




…と、色んな所にブレーク張りつつ調査した結果、ClassInitialize でブレークした時点で TestMethodDeploymentItem で指定したファイルが [TestResults] フォルダ以下にコピーされてるっていうねもうね。 DeploymentItemさんおめーその関数呼び出した時にコピーしてるんじゃねーのかよ!!おのれ!!

というわけで、ファイルはVisualStudioでテスト開始した際にコピーされると。そのタイミングであれば、Visual Studio 辺りがニャンヤンしてファイルコピーできそうですねと。はい解散。

*1:テストで何言ってんだおめーとか聞こえないー聞こえないー

*2:というか普通というか

*3:irem!

PLINQ の Aggregate() で死亡

RGBの色データが入った画像データ配列(正確にはunsafe中のポインタ)を集計したい。



…というシーンが発生。 for でぐるぐる回して集計するのもアリといえばアリですが…当然ながらカッコ悪い。C# なら黙って LINQ だろゴルァと*1。丁度、集計する為の関数 Aggregate() も定義されているし、なにより AsParallel() で手軽に並列化も可能と聞いた! やったねタエちゃん! 暇してるCPUコアに鞭打てるよ!!



とはいえ



集計方針が RGB 個別にニャンヤンする感じなので、seed に R,G,B メンバを備えたクラスを new RGB() して渡すわけですが……参照型渡したら、それ、並列処理できんの…? どう考えてもできないよね…? それとも、PLINQが超賢くなんかやってくれるの…?

正直良くわからんので、とりあえずググったり実験的なコード書いてみた所、最終的にこうなった!

パラレったー

threadIDs の要素数がちゃんとCPUコア数とおなじになったよ! 処理時間も体感レベルで明らかに短かったよ!! すごい!!

class Result
{
    public long R = 0;
    public long G = 0;
};

static public void test()
{
    const int LENGTH = 10001;
    int[] arr = new int[LENGTH];
    for (int i = 0; i < LENGTH; ++i)
    {
        arr[i] = i;
    }

    Result r = ParallelEnumerable.Range(0, LENGTH).Aggregate(
        () => new Result(),
        (aggr, i) =>
        {
            aggr.R += arr[i];
            aggr.G += ComputeG(arr[i]);
            return aggr;
        },
        (aggr1, aggr2) => new Result { R = aggr1.R + aggr2.R, G = aggr1.G + aggr2.G },
        (aggr) => aggr
    );

    arr[0] = 0; // ブレークポイント設定用の何か
}

static public long ComputeG(int i)
{
    threadIDs.Add(System.Threading.Thread.CurrentThread.ManagedThreadId);
    System.Threading.Thread.Sleep(1);
    return i;
}

private static HashSet<int> threadIDs = new HashSet<int>();

失敗 // 最初に書いたコード

なんも考えずに「AsParallel() したらとりあえず並列処理してくれるんでしたっけー?」的に書いたコード。 threadIDs の要素が1つで死ぬ。 シリアルじゃねーじゃねーか!

class Result
{
    public long R = 0;
    public long G = 0;
};

static public void test()
{
    const int LENGTH = 10001;
    int[] arr = new int[LENGTH];
    for (int i = 0; i < LENGTH; ++i)
    {
        arr[i] = i;
    }

    Result r = Enumerable.Range(0, LENGTH).AsParallel().Aggregate(
        new Result(),
        (aggr, i) =>
        {
            aggr.R += arr[i];
            aggr.G += ComputeG(arr[i]);
            return aggr;
        }
    );

    arr[0] = 0; // ブレークポイント作る用の何か
}

static public long ComputeG(int i)
{
    threadIDs.Add(System.Threading.Thread.CurrentThread.ManagedThreadId);
    System.Threading.Thread.Sleep(1);
    return i;
}

private static HashSet<int> threadIDs = new HashSet<int>();

失敗 // ParallelEnumerable に期待する

Enumerable じゃなくて ParallelEnumerable なんてのがあるんスか!こっちじゃないと根本の Range() が並列化しないとかそんな話すかもしや!? と期待して実行。 threadIDs の要素が1つで死ぬ。 クソぁ!!

class Result
{
    public long R = 0;
    public long G = 0;
};

static public void test()
{
    const int LENGTH = 10001;
    int[] arr = new int[LENGTH];
    for (int i = 0; i < LENGTH; ++i)
    {
        arr[i] = i;
    }

    Result r = ParallelEnumerable.Range(0, LENGTH).Aggregate(
        new Result(),
        (aggr, i) =>
        {
            aggr.R += arr[i];
            aggr.S += ComputeS(arr[i]);
            return aggr;
        },
    );

    arr[0] = 0; // ブレークポイント作る用の何か
}

static public long ComputeS(int i)
{
    threadIDs.Add(System.Threading.Thread.CurrentThread.ManagedThreadId);
    System.Threading.Thread.Sleep(1);
    return i;
}

private static HashSet<int> threadIDs = new HashSet<int>();

ってか

やっぱり seed に与えてる new Result() じゃ無理ですよね…とぐぐり始めた所 Parallel Aggregation | Microsoft Docs の "Using PLINQ Aggregation with Range Selection" を発見。 サンプルソースを見れば一発でした。 なるほど。

ちなみに ParallelEnumerable.Aggregate Method (System.Linq) | Microsoft Docs を使ってるわけですが、なんかもうまず見た目でマジうっへりなんですけどこのリファレンス






いやぁ、PLINQ って本当にいいものですね(※流れてくる哀愁ただようトランペットBGM

*1:※偏見です