ログ吐き骨組み

Microsoft Silverlight を入手

デモ大事。Subscribeの時にConsole.WriteLine並べて、実行結果想像つきますよね、というのがいまひとつすぎたので、出力が見える骨組みを作りました。今後のReactive Frameworkの紹介時にソースコード上のDebug.WriteLineは、こーいうことなんですねー、と思ってください。毎回これ乗っけてると長ったらしいので、暗黙の、ということで。

<UserControl x:Class="SilverlightApplication4.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="200" />
        </Grid.ColumnDefinitions>

        <StackPanel Grid.Column="0">
            <Button Name="ExecuteButton" Content="Execute" />
            <Button Name="ErrorButton" Content="Error" />
            <Button Name="ObservableButton" Content="Observable" />
            <Button Name="EnumerableButton" Content="Enumerable" />
        </StackPanel>
        <ScrollViewer Grid.Column="1">
            <TextBlock Name="LogBrowseTextBlock"></TextBlock>
        </ScrollViewer>
    </Grid>
</UserControl>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Windows.Controls.Primitives;
using System.Reflection;

namespace SilverlightApplication4
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            Debug.Set(LogBrowseTextBlock, Dispatcher);

            ExecuteButton.GetClick().Subscribe(() =>
                Observable.Range(1, 10).Subscribe(
                    i => Debug.WriteLine(i),
                    e => Debug.WriteLine(e),
                    () => Debug.WriteLine("completed")
                )
            );

            ErrorButton.GetClick().Subscribe(() =>
                Observable.Range(1, 10)
                    .Do(i => { if (i == 5) throw new Exception(); })
                    .Subscribe(
                        i => Debug.WriteLine(i),
                        e => Debug.WriteLine("onError"),
                        () => Debug.WriteLine("onCompleted")
                    )
            );

            ObservableButton.GetClick().Subscribe(() =>
                GetMethodNames(typeof(Observable)).ToList().ForEach(s => Debug.WriteLine(s))
            );

            EnumerableButton.GetClick().Subscribe(() =>
                  GetMethodNames(typeof(Enumerable)).ToList().ForEach(s => Debug.WriteLine(s))
            );
        }

        IEnumerable<string> GetMethodNames(Type type)
        {
            return type.GetMethods(BindingFlags.Static | BindingFlags.Public)
                .Select(mi => mi.Name)
                .OrderBy(s => s)
                .Distinct();
        }
    }

    public static class Debug
    {
        private static TextBlock textBlock;
        private static Dispatcher dispatcher;

        public static void Set(TextBlock textBlock, Dispatcher dispatcher)
        {
            Debug.textBlock = textBlock;
            Debug.dispatcher = dispatcher;
        }

        public static void WriteLine(object message)
        {
            if (textBlock != null)
            {
                dispatcher.BeginInvoke(() =>
                    textBlock.Text = message.ToString() + Environment.NewLine + textBlock.Text);
            }
        }
    }

    public static class ControlExtensions
    {
        public static IObservable<Event<RoutedEventArgs>> GetClick(this ButtonBase button)
        {
            return Observable.FromEvent<RoutedEventArgs>(button, "Click");
        }
    }
}

幾ら簡易的なものだから、という言い訳がましさを考えても、かなり微妙なコードな気がする。原型はWPFでTraceListenerにTextBlockに書きだすものを追加して、Trace.WriteLineで処理していたもの。SilverlightにはTraceがなかったので、そのまま静的クラス・メソッドに置き換えて、Silverlightにも本来あるDebugを塗り替えちゃうという……。ようするにそのままコピペしても動くよね!的な感じでやりたいな、というところなわけです。ダメ?

ついでにSubscribeの中でObservableって、汚い、ように見えるかも。でも実際これはなんてことなくて、ようはJavaScriptっぽいんですよね、ほんと。DOMContentLoadedにイベント登録のaddEventListener並べるのと一緒で。じゃあ実際こうしてグチャグチャ並べるかというとそうではないようで、実際は拡張メソッドへ記述する、という形で分散していくようですが、まだまだ分からず。Microsoft側の実例やドキュメントが整ってくれないと何とも言えない感じ。

作業環境

画像クリックで原寸サイズ。最近思うところあってVisual Studioの配置をごにょごにょと弄っています。今は、こんな感じに落ち着きました。左にエラー一覧・検索など。右にソリューションエクスプローラー・クラスビュー・スタートページなど。そして左右にそれぞれコードウィンドウを分割。原則的にメインウィンドウは左。コード定義ウィンドウを右ウィンドウに開いて常時表示。もしくはXAML編集と並列したり。といったところです。コード定義ウィンドウはデカい画面で常時表示で初めて効果を発揮しますね、素晴らしく便利。

30インチ 2560x1600の無駄遣いが火を吹く!というわけですが、やっぱ広いって便利、エディタウィンドウ2面同時表示って便利、です。30インチでなくても、横2560は19インチ1280x1024のデュアルで行けます。ただ、実際はこれに加えてデバッグ時のプログラム本体なりブラウザなりを置いておく場所が欲しいので、その場合はデュアルじゃ足りないですね……。グラフィックボードが一枚でトリプルをサポートしてくれれば、というか、するべき、ですよね。ATIのEyefinityにはとても期待してます。

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

Microsoft MVP for Developer Technologies(C#)
April 2011
|
July 2024

Twitter:@neuecc GitHub:neuecc

Archive