忍者ブログ
おそらく活動状況とか、他愛もないこととか書きます。 μ崎みのりの気ままで気まぐれなブログです。 作曲とか、ゲーム製作なんかしてます。
[1]  [2]  [3]  [4]  [5]  [6]  [7]  [8
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

少し前に書いた自分のコードで面白いのがあったのでそれについて考察。
まずは要点を抜き出したコードから。
MyEventHandler はデリゲート型です。

class ListnerClass
{
   
// 引数eventListnerSetterは、「イベントをフックしてもらう処理」を
    // デリゲートとして受け取る。
    public ListnerClass(Action<MyEventHandler> eventListnerSetter)
    {
        eventListnerSetter(
Parent_MyEvent);
    }

    private void Parent_MyEvent(object sender, EventArgs e)
    {
        // イベントに対する処理
    }
}

class ObserberClass
{
    public event MyEventHandler MyEvent;
    ListnerClass listnerObject;

    public ObserberClass()
    {
        listnerObject = new ListnerClass(listner => MyEvent += listner);
    }
}

デリゲートが入れ子になっていて回りくどいですが、
最終的には ObserberClass.MyEventListnerClass.Parent_MyEvent がフックされます。
普通にイベントをフックするだけなら

obserberObject.MyEvent += Parent_MyEvent;

とでも書けば十分ですよね。
今回それをせずにわざわざ「デリゲートを受け取るデリゲート」を受け取るコンストラクタを定義しているのには理由があります。

ListnerClass は所有される側のクラスで、所有するクラスを知らなくていい設計にしたい
ListnerClass はひとつの親オブジェクトに複数含まれる場合がある
Parent_MyEvent メソッドは公開したくない

この3点が大きな理由でしょうか。
なんにせよ、このような形にすることでprivateなメソッドを限定された範囲で間接的に呼び出してもらったり、知らない誰かのイベントを処理したりできるようになります。

デリゲートを使用するようなデザインパターンはあまり語られることがありませんが、
使いこなせば非常に有用だと思ったコードでした。
PR
おひさしぶりです。
引越しがあり、しばらくインターネット環境が無い状態で過ごしていました…。


さて、C#(というか.NET)では、ジェネリクスの型パラメータに制約を課すことができます。

class GenericClass<T> where T : MyClass
{ … }

こんな感じで、このクラスの型パラメータTには、MyClassを継承した型しか指定できなくなります。
……で、これの変わった、というかちょっと面白い使い方について。

class Hoge<T> where T : Hoge<T>
{ … }

Hogeクラスの型パラメータTには、このクラス自身を継承している型しか指定できないように制約しています。
……果たしてこの制約に意味はあるのか?というのが今回の話題。


その例がこちら。

abstract class SuperClass
{
    public SuperClass Child { get; set; }
    public void SuperMethod()
    {
        this.Child.SuperMethod();
    }
}

class SubClassA : SuperClass
{
    public void SubMethod()
    {
        this.Child = new SubClassA();
        …
        this.Child.SuperMethod();
        this.Child.SubMethod();  //ChildはSuperClass型として定義されているのでこれは無理!
        ((SubClassA)this.Child).SubMethod();  //キャストするしかない…

        this.Child = new SubClassB();  //プログラムの仕様上は正しくないが、コンパイルは通る
    }
}

class SubClassB : SuperClass
{ … }


たとえばこんな場合。
ここで、SubClassAではChildプロパティにはSubClassA型のインスタンスが格納され、SubClassBではChildプロパティにSubClassB型のインスタンスが格納されるとします。

……しかしこの仕様では、ChildプロパティはSuperClass型として定義されており、上述のルールを守るにはプログラマが気を付ける必要があります(SubClassAのChildプロパティに、SubClassBを代入できてしまう)。


これを解決する方法のひとつとして、自身の継承を制約とするジェネリクスが使えます。

abstract class SuperClass<T> where T : SuperClass<T>
{
    public T Child { get; set; } //ChildプロパティをT型で定義
    public void SuperMethod()
    {
        this.Child.SuperMethod();  //T型の制約により、SuperClassのメンバにアクセスできる
    }
}

class SubClassA : SuperClass<SubClassA>
{
    public void SubMethod()
    {
        this.Child = new SubClassA();
        …
        this.Child.SuperMethod();
        this.Child.SubMethod();  //Childは型引数によりSubClassA型なのでこれが可能

        this.Child = new SubClassB();  //型が違うのでコンパイルエラーとなる
    }
}

class SubClassB : SuperClass<SubClassB>
{ … }

前のコードと変わったのは、SuperClassに型パラメータがついたことと、Childプロパティがその型パラメータの型として定義されていることです。
TはSuperClass<T>の派生クラスであることが制約として課されているので、当然SuperClass内ではChildを通じてSuperClassのメンバにアクセスできます。

で、SubClassAがSuperClassを継承する際には、型引数にSubClassA自身を渡します。
Childプロパティはこの型引数の型として定義されてますから、SubClassAのChildプロパティはSubClassA型になります。
これで、プログラマが気を付けなくても、SubClassAのChildプロパティにはSubClassAしか代入できなくなりました。


とまぁこのように、「自分自身の派生クラスを制約とする型パラメータ」は、型安全性の強化として使えるよーという話です。

ただこのパターンには弱点があって、ジェネリクスを使用しないほうのコードではSuperClass型の変数でSubClassA・SubClassBのどちらのインスタンスも受け取ることができますが、ジェネリクスを使用したコードではそれができなくなります(SubClassA・SubClassBの両方を受け取りたい場合、SuperClass<T>の上にさらに非ジェネリックな親クラスを作ることに…)。


私がプログラミングしていて、実際にこのパターンを使う場面に遭遇したので書いてみました。
階層的なデータ構造を表すものの一部の振る舞いが異なるクラスが2種類あって、それらの機能を抽象化してまとめるのに使用しました。
(このように、今回のパターンは汎化の段階で有用になるかもしれません)
WPFでカスタムコントロールを作る時、
各コントロールの既定のスタイルを知りたいことがよくあると思うのですが、
動的に(コードから)取得しようとするとなぜかうまくいきません。

具体的には、

<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}" />

こんな感じで明示的に規定のスタイルを継承した空のスタイルを定義して、
BasedOnプロパティを参照して得てみようと考えました。

で、これに相当するC#コードを書こうとするんですが、

BasedOn
="{StaticResource {x:Type Button}}"


この部分がどうやっても簡単には書けないんですよね…。
Xamlならこんなに単純なのに。

紆余曲折あり、以下の手順でなら既定のスタイルを取得することに成功しました。

・Controlクラスを継承する全てのpublicな非抽象クラスを列挙する。

・↓に相当するxaml(の文字列)を生成し、リソースファイルのxamlコードに手動で貼りつける。
<Style TargetType="{x:Type 型名}" BasedOn="{StaticResource {x:Type 型名}}" />

DataContextに作成したリソースを読み込み、ItemsControlなんかで表示すればOK。


この方法でなら、読み込んだStyleオブジェクトのBasedOnプロパティが
ちゃんと既定のスタイルを参照しています。
このままではうまく表示できませんから、System.Windows.Markup.XamlWriterなどを使って
必要な部分をXamlに変換してあげればOK。


全然スタイリッシュじゃない上に他テーマのスタイルが取得できないなど問題だらけですが、
とりえあず取得はできたので良しとしましょう…。

====================追記====================
MSDNによれば、既定のスタイルはアセンブリ内の特定の場所にxamlファイルを置いて記述するようなので、リフレクションを使えば動的に取得できそうな気もします。
要検証。
多趣味で色々なものに手を出しているμ崎です。

最近は関数型プログラミング言語や、数学についてといった
いかにもドヤ顔ができそうな内容について色々勉強していましたが、
今の私のトレンドはフラクタル図形に移りました。

……そうです、今回も幾何学です。



一度くらいは、このような図形を見たことがあるんじゃないでしょうか。
三角形の真ん中を白抜きにして、穴で分割された3つの小さな三角形の真ん中を白抜きにして、
さらにその穴で分割された三角形を……といった操作を延々と続けていくとできるこの図形は、
シェルピンスキーのガスケットと呼ばれます。
実際にはこの操作を無限回繰り返した図形なので、正確には作図できません。

このような図形の一部分が全体と自己相似しているような図形をフラクタルと呼ぶのですが…

この図形、約1.6次元の図形なんです。

ここでいう「次元」という言葉は、誰もが思い浮かべる「次元」とは少し違うのですが。
やっぱり普通じゃない図形って私の目には魅力的に映るんですよね……。

で、理論的な話はWikipediaを参照してもらうとして(投げやり)、
今私はこのような(再帰的に定義できる)フラクタル図形を表示するソフトを作っています。
数学ができない人でもなんとなくドラッグの操作で遊べるように頑張ってます。
最初は遊びのつもりでしたが結構な完成度になってきたので、完成したら公開しようと思います!
最近、よくタイトルのようなネット広告を見かけます。
内容を見ると、4桁の整数のうち、素因数分解した際の"×"の数と、各桁の数の和が等しくなるような数を求めるプログラムを書け、というもの。

応募するわけではないのですが、興味が湧いたので書いてみることに。
そのコードを載せるわけですが、出題側はカンニングされては困ると思うので、
無駄にトリッキーな技を使ったりしてコード量を極限まで切り詰めてみました。
単なるネタです。

public static void Main()
{
    Func<int, int>
f = nullg =
        n => Enumerable.Range(2, n - 2).FirstOrDefault(m => n % m == 0);
    f = n => g(n) == 0 ? 0 : 1 + f(n / g(n));
    Console.WriteLine(string.Join("\n", Enumerable.Range(1000, 9000)
        .Where(n => f(n) == ("" + n).Select(m => m - 48).Sum())));
}


はい、わずか3ステートメント。コードが短くなるように変数名も1文字にしました。
ある意味では最適解ですが、どう考えても実務では叱られますね(笑)
コードを短くするためにかえって無駄な処理があったりしますし、実行効率は気にしていません。
メソッド部分のみ書いたので、実行する際は適当にクラスで覆って下さい。
忍者ブログ [PR]