2016年5月31日

C# 文字列リテラル(補間文字列・逐語的文字列)まとめ

C# 6.0 から補間文字列が追加されて、文字列リテラルの種類が 4 つに増えている。
MSDN にまとめた記事が無いので(たぶん)、自分用まとめ

書式 バックスラッシュ
エスケープ
改行 補間式 その他の
エスケープ
標準 "regular"
逐語的 @"verbatim
string"

"" → "

補間 $"1+1={1+1}"

{{ → {

}} → }

補間
逐語的
$@"interpolated
{"and"}
verbatim"

"" → "

{{ → {

}} → }


また、補間文字列の補間式の中に各種文字列リテラルを書くことが可能。
つまり、補間文字列のネストも可能。(実用性は無さそうだけど)
ただし、改行を許可しない補間文字列の中に、逐語的文字列や補間逐語的文字列を書いても改行を含めることはできない。(コンパイルエラー)

// 逐語的(改行無し) in 補間 : OK
var nested1 = $"this is {@"verbatim string"}";

// 逐語的(改行有り) in 補間逐語的 : OK
var nested2 = $@"this is {@"verbatim
string"}";


// 逐語的(改行有り) in 補間 : ERROR
var nested_error = $"this is {@"verbatim
string"}";

参考URL

2016年5月25日

Kotlin から見た Java の getter / setter (getXXX / isXXX / setXXX)

Calling Java code from Kotlin - Getters and Setters の補足みたいな話。
Kotlin 1.0.2

Kotlin から Java のコードを呼ぶ際、getter / setter (getXXX / isXXX / setXXX メソッド) はプロパティとして扱える。
詳細なルールとしては、
getter
  • get{X} または is{X} で始まる引数無し・戻り値ありのインスタンスメソッド
    {X} は小文字と解釈されない文字、日本語でもOK
  • getXXX / isXXX でプロパティ名の規則が異なる
    • getHoge メソッド → hoge プロパティ
    • isHoge メソッド → isHoge プロパティ
  • isXXX の戻り値の型は問わない (Boolean 以外でも可)
setter
  • set{X} で始まる引数 1 つのインスタンスメソッド
  • 戻り値の有無は問わない (あってもいい)
  • 対になる getter が必要 (Kotlin は今の所 set-only プロパティをサポートしてないため)

[ルールのサンプル (Java)]

// Kotlin でプロパティになるケース (メソッドとして使用できない)
public class PropertySample {
    public String getName() { /* 略 */ }
    public void setName(String name) { /* 略 */ }
    // [Kotlin] public final var name: String!
    // getter/setter があるので var プロパティ

    public Boolean isKey() { /* 略 */ }
    public void setKey(Boolean key) { /* 略 */ }
    // [Kotlin] public final var isKey: Boolean!
    // getter が isXXX の場合、プロパティ名は同じ(isXXX)

    public Boolean getKey() { /* 略 */ }
    // [Kotlin] public final var key: Boolean!
    // getter が isXXX と getXXX の 2 つ存在する場合、プロパティも 2 つになる
    // setter は共通 (両方のプロパティから使用される)

    public int getId() { /* 略 */ }
    // [Kotlin] public final val id: Int
    // getter のみは val プロパティ

    public String getあ() { /* 略 */ }
    public String setあ(String あ) { /* 略 */ }
    // [Kotlin] public final var あ: String!
    // 日本語可、setter に戻り値があっても可
}

// Kotlin でプロパティにならないケース (メソッドとして使用できる)
public class NotPropertySample {
    // setter のみ
    public void setNum(int num) { /* 略 */ }

    // 小文字スタート
    public int geta() { /* 略 */ }

    // static メソッド
    public static int getStatic() { /* 略 */ }

    // プロパティ名がかぶる
    public String getUrl() { /* 略 */ }
    public String getURL() { /* 略 */ } // 全部大文字の場合、プロパティ名は小文字になる
}

雑記

isXXX は混乱の元になりそうなのであまり使いたくないところ。
でも Kotlin から Java を呼び出すケースって、おそらく既存ソースの再利用で isXXX がある可能性もそれなりにあって・・・

ちなみに Java から Kotlin のプロパティを使用する際は、逆のイメージ。
  • XXX プロパティ → getXXX / setXXX メソッド
  • isXXX プロパティ → isXXX / setXXX メソッド

2016年3月23日

Kotlin null 許容型 (nullable) の外し方(スマートキャスト)

Kotlin の null 許容型 (nullable type) の変数は、if 等の制御文を挟むことで、null 非許容型 (non-null type) に自動でキャストされる。
null 許容型はコンパイル時にしか存在しないので、バイトコードにキャスト処理が入るわけではない。
IntelliJ IDEA だと、自動キャストされた変数はハイライトされて、マウスオーバーすると「Smart cast to 型名」がポップアップされるので分り易い。

ここでは、どんな制御文で null 許容型からのスマートキャストが可能か(コンパイラがどこまでやれるのか)、色々試してみる。
Kotlin 1.0.1 なので、バージョンが上がると挙動が変わるかも。

1 変数の場合

  1. if で null 除外
    
    fun ifNotNull(x: Any?) {
        if( x != null ) println(x.javaClass)
    }
    
    基本。
  2. if で null の場合、return
    
    fun ifNullReturn(x: Any?) {
        if( x == null ) return
        println(x.javaClass)
    }
    
    (1) の逆バージョン。
  3. when で null 除外
    
    fun whenNotNull(x: Any?) {
        when( x ){
            null -> {}
            else -> println(x.javaClass)
        }
    }
    
    (1) の when バージョン。if 使った方がいい。
  4. when で null の場合、return
    
    fun whenNullReturn(x: Any?) {
        when( x ){
            null -> return
            else -> {}
        }
        println(x.javaClass)
    }
    
    (2) の when バージョン。これも if 使った方がいい。
    また、else を省略するとコンパイルエラー( .javaClass で)。
  5. エルビス演算子 ?: で null の場合、return
    
    fun elvisNullReturn(x: Any?) {
        x ?: return
        println(x.javaClass)
    }
    
    (2) のエルビス演算子バージョン。
    少し魔術っぽいけど、慣れたら問題ない・・・? タイプ量が少なくなるから使いそう。
    return の代わりに例外 throw も可。
  6. ぬるぽ演算子 !! で null の場合、例外
    
    fun nullpo(x: Any?) {
        x!!
        println(x.javaClass)
    }
    
    ほとんどの場合、バッドプラクティス。(ぬるぽ演算子の使い所は難しい・・・)
    !! 演算子は特に名前が無いようなので、"ぬるぽ演算子"の呼び方は非公式
  7. while で null 除外
    
    fun whileNotNull(x: Any?) {
        while( x != null ){
            println(x.javaClass)
            break
        }
    }
    
    (1) の while バージョン。使い所はあまり無い気がするけど、一応可能。
  8. while で null の場合、return
    
    fun whileNullReturn(x: Any?) {
        while( x == null ) return
        println(x.javaClass)
    }
    
    (2) の while バージョン。使い所なんて無い気がするけど、一応可能。
  9. [NG] if-true ネストで if-return
    
    fun ifNullReturnIfNest(x: Any?) {
        if( true ){
            if( x == null ) return
        }
        println(x.javaClass) // コンパイルエラー
    }
    
    ありえないコードは、ネストの中まで見てくれない模様。
  10. while-true ネストで if-return
    
    fun ifNullReturnWhileNest(x: Any?) {
        while( true ){
            if( x == null ) return
            break
        }
        println(x.javaClass)
    }
    
    if と異なり while ならネストの中まで見てくれる・・・。
    while の条件を変数にすると、コンパイルエラー。
    たぶん、while-true がありえるかもしれないコードだからだと推測。
  11. [NG] when-true ネストで if-return
    
    fun ifNullReturnWhenNest(x: Any?) {
        when {
            true -> if( x == null ) return
        }
        println(x.javaClass) // コンパイルエラー
    }
    
    これもありえないコードなので不可。when に else があっても不可。
  12. [NG] with 関数のラムダ式で if-return
    
    fun ifNullReturnWithScope(x: Any?) {
        with( x ){
            if( x == null ) return // この return は ifNullReturnWithScope を抜ける
    
            // 本来、with 内で x にアクセスする際は this を使う
        }
        println(x.javaClass) // コンパイルエラー
    }
    
    VB でお馴染みの With だが、Kotlin では関数 + ラムダ式になっている。(組み込みの構文ではない。)
    with 関数はインライン展開されるけど、さすがに関数の中まで見てくれない。

2 変数の場合

  1. if で null 除外
    
    fun ifNotNull(x: Any?, y: Any?) {
        if( x != null && y != null ){
            println(x.javaClass)
            println(y.javaClass)
        }
    }
    
    単純な複合条件は可能。
    引数無し when でも同様に可能。
  2. if で null の場合、return
    
    fun ifNullReturn(x: Any?, y: Any?) {
        if( x == null || y == null ) return
        println(x.javaClass)
        println(y.javaClass)
    }
    
    (1) の逆バージョン。
    引数無し when (else 有り) でも同様に可能。
  3. if-else-if で順番に null 除外
    
    fun ifNotNullEach(x: Any?, y: Any?) {
        if( x == null ){
            // x は null、y は null かも
        } else if( y == null ){
            // x は 非 null、y は null
            println(x.javaClass)
        } else {
            // x、y ともに非 null
            println(x.javaClass)
            println(y.javaClass)
        }
    }
    
    一度 null を除外すれば、次のブロックでも有効。
    引数無し when でも同様に可能。
  4. [NG] if-else-if で順番に複合条件で null 除外
    
    fun ifNotNullComplex(x: Any?, y: Any?) {
        if( x == null && y == null ){
            // x、y ともに null
        } else if( x != null && y == null ){
            // x は 非 null、y は null
            println(x.javaClass)
        } else if( x == null && y != null ){
            // x は null、y は 非 null
            println(y.javaClass)
        } else {
            // x、y ともに非 null のはず
            println(x.javaClass) // コンパイルエラー
        }
    }
    
    コンパイラが複雑になるから見てないのだと推測。特に変数が増えた場合やばそう・・・
    人間が見ても else ブロックで null 除外されていることは、分かりづらいと思う。

2016年3月10日

Kotlin null 許容型 (nullable) はコンパイル時だけの存在

Kotlin の目玉機能の 1 つ「null 安全」のために null 許容型 (nullable type) が存在するが、これはコンパイル時だけの存在で、実体クラスが定義されているわけではない。
C# / VB.NET 経験者なら Nullable<T> 相当のクラスがあるのか?と思うかもしれないが、そんなものは無い。

たぶん、Java コードとの共存 + 実行時のパフォーマンスが考慮された仕様になっている。

一応、下記で検証可能。

fun main(args: Array<String>) {
    printType<Int>()
    printType<Int?>()
    // Int?::class だとコンパイルエラー (直接 println できない)
}

inline fun <reified T> printType() {
    println(T::class)
}

//[結果]
// class kotlin.Int
// class kotlin.Int
※ 2 行目と 3 行目で生成されるバイトコードは同じ

検証環境

  • jdk 1.8.0_74
  • Kotlin 1.0.0

2016年2月12日

Kotlin 遅延評価型と先行評価型のコレクション操作関数

"ことりん" の名前が気に入ったので色々調査中・・・
(・8・) × >ω</

Kotlin のコレクション操作関数(C# の LINQ to Object、Java の Stream API 相当)は、1.0.0-rc-1036 時点で stdlib に 2 種類用意されている。
Sequence インターフェース の拡張関数(遅延評価型)と、Iterable インターフェース の拡張関数(先行評価型)である。
※ スカラ値を返す関数(any, count, max 等)は両方とも先行評価型

よって、正確には、LINQ to Object や Stream API 相当になるのは、Sequence 拡張関数の方になる。

[Sequence と Iterable 拡張関数の比較]

fun main(args: Array<String>) {
    val source = 0..3
    lazyEvaluationMethods(source)
    println()
    eagerEvaluationMethods(source)
}

// 数字リストから偶数のみを抽出して 2 倍する

fun lazyEvaluationMethods(source : Iterable<Int>) {
    var result = source.asSequence().filter {
        println("sequence.filter : $it")
        it % 2 == 0
    }.map {
        println("sequence.map : $it")
        it * 2
    }.toList()
    println(result)
}

fun eagerEvaluationMethods(source : Iterable<Int>) {
    var result = source.filter {
        println("iterable.filter : $it")
        it % 2 == 0
    }.map {
        println("iterable.map : $it")
        it * 2
    }
    println(result)
}
[結果]
sequence.filter : 0
sequence.map : 0
sequence.filter : 1
sequence.filter : 2
sequence.map : 2
sequence.filter : 3
[0, 4]

iterable.filter : 0
iterable.filter : 1
iterable.filter : 2
iterable.filter : 3
iterable.map : 0
iterable.map : 2
[0, 4]

検証環境

  • jdk 1.8.0_74
  • Kotlin 1.0.0-rc-1036

雑記

Sequence と Iterable の違いを知ってないと割と危険な実装をしてしまいそう・・・
Iterable 拡張関数の方は戻り値が kotlin.collections.List 型なのですぐに気付くはず。
その List の実装は、今のところ java.util.ArrayList が利用されている。

2016年2月10日

Kotlin ジェネリクス 型引数の情報を実行時に取得

Kotlin 正式版がそろそろリリースされそうなので・・・
GitHub - temp-impl/spring-mvc-singleton-vs-prototype_kotlin を作った時の調査メモ。)

Kotlin も JVM 言語なので、コンパイル時型消去の呪いからは逃れられない。
が、inline と reified を使うことで、関数・メソッドなら型引数の情報を実行時に使用可能になる。

[型引数使用サンプル]

fun main(args: Array<String>) {
    printType<String>()
}

inline fun <reified T> printType() {
    println(T::class)
}

//[結果]
// class kotlin.String

//[補足]
// 上記結果を得るためには kotlin-refrect.jar も必要
// 無くてもエラー無しで実行できるが、下記結果になる
// class java.lang.String (Kotlin reflection is not available)
[上記のバイトコード(一部)]

   L2
    LINENUMBER 8 L2
    LDC Ljava/lang/String;.class
    INVOKESTATIC kotlin/jvm/internal/Reflection.getOrCreateKotlinClass (Ljava/lang/Class;)Lkotlin/reflect/KClass;
    ASTORE 1
    NOP
   L3
    LINENUMBER 11 L3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 1
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V

inline というキーワードから想像が付くと思うが、関数の中身がインライン展開されている。
そのため、型引数の情報も当然使用可能。

ちなみに Java クラスの情報が欲しい場合

inline fun <reified T : Any> printJavaType() {
    println(T::class.java)
}

//[結果] (上と同じ main)
// class java.lang.String

//[補足]
// 型引数のデフォルト上限は Any? で Java に無いクラスなので、Any 上限を指定する必要がある
// .java は KClass<T : Any> の拡張プロパティ

検証環境

  • jdk 1.8.0_74
  • Kotlin 1.0.0-rc-1036

雑記

inline 関数の型引数はデフォルト reified でも良さそうと思ったが、たぶん Kotlin は(型引数使用を)明示していくスタイルなのだろう。
(コンパイラの都合もあるかもしれない・・・)

Scala の implicit + ClassTag みたいな仕組みはおそらく導入されない。
Comparison to Scala - Kotlin Programming Language を読むと implicit は導入する気が無さそうなので。

2016年1月30日

Spring @Autowired フィールドを持つクラスを手動で生成

初めて Spring を触って、@Autowired の便利さに感動したので・・・

前置き

@Autowired について簡単に説明すると、自動で初期化されるフィールドの目印。
(フィールドだけでなくセッターメソッドに対しても使用可能)
つまり、コンストラクタやフィールドの右辺に、フィールドの初期化処理を書かなくてよくなる。
古い Spring では初期化設定を xml に書く必要があったが、今はアノテーション(C# での カスタム属性)のみで完結できるようになっている。

[@Autowired サンプル]

@Controller
public class HogeController {

    @Autowired
    private HogeService service;

    // 以降、URL 割り当てメソッド
}

@Service
public class HogeService {

    @Autowired
    private HogeDao dao;

    // 以降、サービスメソッド
}

@Component
public HogeDao {}
HogeController にマップされた URL アクセス時、HogeController インスタンスがフレームワークによって生成され、@Autowired フィールドの service にも HogeService インスタンスが割り当てられる。HogeService の dao フィールドも @Autowired なので同様。
DI 目的なら HogeService と HogeDao のインターフェースを用意した方がいいけど、ここでは省略。
@Autowired 可能なクラスは、@Component 派生アノテーション付きで、@ComponentScan 対象である必要がある。(もしくは xml に記述したクラス)

@Autowired 持ちクラスを手動で生成

上記サンプルのような使い方だと、@Autowired 持ちクラスが初期化されるためには、@Autowired フィールドとして生成されるか、@Controller のようにフレームワークから自動生成される必要がある。
つまり、フィールドとしてしか作れない。(@Controller もフレームワークのフィールドみたいなもの)

@Autowired 持ちクラスをフィールドじゃなく、ローカル変数として欲しい(コードで手動生成したい)ケースが稀にあると思う。
java - In Spring, can I autowire new beans from inside an autowired bean? - Stack Overflow の createAutowiredObject で簡単に生成できる。
@Component 付クラスである必要もない。

[createAutowiredObject 使用サンプル]

@Controller
public class HogeController {

    @Autowired
    private AutowireCapableBeanFactory factory;

    private <T> T createAutowiredObject(Class<T> c){
        return factory.createBean(c);
    }

    public void createAutowiredTest(){
        HogeComponent component = createAutowiredObject(HogeComponent.class);
        // HogeComponent のフィールドが初期化されていることを確認
        System.out.println(component.sub);
    }
}

public HogeComponent {
    @Autowired
    public HogeComponentSub sub;
}

@Component
public HogeComponentSub {}

使いどころ

@Component 付きクラスは、デフォルトでシングルトンである。
@Scope("prototype") を付けることで非シングルトンにすることも可能だが、親クラスも同じ @Scope を設定してやる必要がある。(シングルトンのフィールドは同じくシングルトンになる)

親クラスがシングルトンで、その内部で非シングルトンの @Autowired 持ちクラスが欲しい場合、createAutowiredObject のようなメソッドがあるといいかもしれない。
親クラスの @Scope を変えても可能だが、変えられないケースもあると思うので。

おまけ

@Scope の効果を Spring MVC で視覚的に確認できるもの

2016年1月13日

Java Stream API とチェック例外(検査例外)の相性が悪い件

久々に Java を触ることがあって、Java 8 だったので Stream API を使ってみたところ・・・
色々言いたいことはあるけど、一番の問題はチェック例外(検査例外)だと感じた。
チェック例外とは、簡単に言うと catch または throws してないとコンパイラに怒られる例外のこと。
Java 以外の言語ではほとんど採用されてないレア機能。

で、何が問題かというと、下記のようなケースでコンパイルできない。

/**
 * source にある valueType 型を返すメソッドを実行して、結果をリストで返す。
 */
@SuppressWarnings("unchecked")
public static <T> List<T> getValues(Object source, final Class<T> valueType){
    return Arrays.stream(source.getClass().getDeclaredMethods())
        .filter(m -> m.getReturnType().equals(valueType))
        .map(m -> (T)m.invoke(source)) // この行でコンパイルエラー
        .collect(Collectors.toList());
}

理由は、Stream.map がチェック例外を投げるラムダ式を引数に取れないため。

※ チェック例外を考慮した FunctionalInterface が標準 API に用意されてないこと(2016/1/12 時点)を考慮すると、ラムダ式にチェック例外を混ぜてほしくないのだろう。しかし、標準 API の中でチェック例外を投げるメソッド(上記の Method.invoke 等)は結構あるため、使う側としては混ぜざるを得ない。

検査例外を実行時例外にラップ

ラムダ式内でチェック例外をキャッチ、実行時例外にラップする。

@SuppressWarnings("unchecked")
public static <T> List<T> getValuesWrap(Object source, final Class<T> valueType){
    return Arrays.stream(source.getClass().getDeclaredMethods())
        .filter(m -> m.getReturnType().equals(valueType))
        .map(m -> {
            try{
                return (T)m.invoke(source);
            } catch( IllegalAccessException | InvocationTargetException e ){
                throw new RuntimeException(e);
            }
        })
        .collect(Collectors.toList());
}

この方法だとチェック例外を殺してしまうが、標準 API のみだとどうしようもない。
また、ラムダ式に try-catch が入り込んでソースが汚くなっている。

チェック例外を投げるラムダ式から標準 API のラムダ式に変換

lambda - How can I throw CHECKED exceptions from inside Java 8 streams? - Stack Overflow にある LambdaExceptionUtil を使う。(もしくは自作)

@SuppressWarnings("unchecked")
public static <T> List<T> getValuesRethrow(Object source, final Class<T> valueType){
    return Arrays.stream(source.getClass().getDeclaredMethods())
        .filter(m -> m.getReturnType().equals(valueType))
        .map(rethrowFunction(m ->  (T)m.invoke(source)))
        .collect(Collectors.toList());
}

rethrowFunction で、チェック例外を考慮した Function_WithExceptions から 標準 API の Function に変換している。
チェック例外を殺すことには変わりないが、ソースの見た目は最初の方法よりましになっている。
チェック例外は不要、という場合はこの方法が無難。
Stream API だけでなく、他のチェック例外無しラムダ式を引数にするメソッドにも利用できる。

後、面白いのが下記のような方法で実行時例外にラップせずにチェック例外を黙らせていること
Java の割と知られているハックらしい。

@SuppressWarnings("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
    throw (E)exception;
}

チェック例外を投げるラムダ式を引数に取れる Stream API を使う

JeffreyFalgout/ThrowingStream · GitHub を使う。(もしくは自作)

@SuppressWarnings("unchecked")
public static <T> List<T> getValuesThrowing(Object source, final Class<T> valueType) throws Exception {
    return ThrowingStream.of(Arrays.stream(source.getClass().getDeclaredMethods()), Exception.class)
        .filter(m -> m.getReturnType().equals(valueType))
        .map(m ->  (T)m.invoke(source))
        .collect(Collectors.toList());
}

ThrowingStream.of でチェック例外を考慮したメソッドを備える Strem を作成している。
現時点(2016/1/12)で、ThrowingStream.of に指定できる例外は 1 つだけなので、複数のチェック例外がある場合は継承元を指定するしかない。
指定した例外を ThrowingStream.collect で投げてくれるので、一応チェック例外は殺してない。

愚痴

この問題については、Java 開発元がどうにかすべきだと思う。
上記のようなメソッド・クラスを用意するか、いっそコンパイルオプションにチェック例外無効を用意するとか・・・

いずれ解決方法が提供されることを祈りつつ、可能ならベター Java を使っていきたい・・・

2015年12月5日

Firefox のボタン(<button>, <input type=button>)の大きさ(幅・高さ)が違う場合の対処法

width や height を指定せず、padding や内部の文字列で大きさを確保するボタン(<button> や <input type="button">)では、IE/Chrome と比べて Firefox のボタンサイズが大きくなる。
文字の幅は、同じフォントを指定してもブラウザによって異なるのが普通だが(レンダリングエンジンが異なるため)、Firefox のみ明らかに大きくなる。

[問題になる CSS の例]

button {
    padding: 5px;
    border: 1px solid lime;
    letter-spacing: 0px;

    /* ブラウザ間の差が少ないメイリオを使用 */
    font: 15px/22px Meiryo;
    cursor: pointer;
}
[プレビュー]
[開発者ツールの画面キャプチャ]
IE Chrome Firefox (Firebug)
IE button inspection Chrome button inspection Firefox button inspection
※ Firefox のボタンサイズが、縦方向 2px、横方向 6px 大きい。

原因と対処法

困った時の stackoverflow。
Firefox はボタンフォーカス時の点線の幅と padding が確保されているため、他のブラウザより大きくなるのだとか。
これ → Firefox focused button

[ボタンフォーカスの点線を消す CSS]

button::-moz-focus-inner {
    padding: 0;
    border: 0;
}
/* input[type=button] なども同様に指定可能 */
[プレビュー]
[Firebug の画面キャプチャ]
Firefox button inspection

ボタンフォーカスの点線を消したくない場合、margin を工夫することで大きさを合わせることが可能。

button::-moz-focus-inner {
    padding: 0;
    margin: -1px; /* 点線の幅分のマイナスマージン */
}

ただし、最初にも書いたが、ブラウザによる文字の幅の差はどうすることもできないので、ボタンの大きさ完全に一致させたい場合は、button の width を指定するしかない。

検証環境

  • Windows 7 64bit
  • Internet Explorer 11
  • Firefox 42.0
  • Chrome 48.0.2564.8

2015年11月19日

<iframe> を動的生成する際の注意点

javascript で src 無しの <iframe> とその中身を構築した際、はまったことがあったのでメモ。

  • 動的に追加した <iframe> の中身をいじる際は、
    iframe.contentWindow.documentdocument.write() を使う
    または
    <iframe> のロード完了を待つ。(<iframe> 自身に onload イベントがある)
  • <iframe> に onload イベントを設置する際は、DOM への追加前に行う。
    DOM に <iframe> を追加した瞬間、onload イベントが処理されるブラウザがあるため (Chrome)

[サンプルコード]

// 単純化のため、jquery 使用

// 良い例
// IE:OK / Firefox:OK / Chrome:OK
$(function(){
    $('<iframe>').load(function(){
        // ロード完了後、DOM を変更できる。
        var ibody = this.contentWindow.document.body;
        $(ibody).append('<div>ブロック要素</div>');
    }).appendTo('body');
    // イベント設置後に DOM へ追加
});


// 悪い例(1)
// IE:OK / Firefox:NG / Chrome:OK
// Chrome は iframe の DOM 追加後、即時ロード完了する模様
// IE は iframe のロード完了を待たなくても大丈夫っぽい??
$(function(){
    var iframe = $('<iframe>').appendTo('body');
    var ibody = iframe[0].contentWindow.document.body;
    $(ibody).append('<div>ブロック要素</div>');
});


// 悪い例(2)
// IE:OK / Firefox:OK / Chrome:NG
// Chrome は iframe が即時ロード完了するので、DOM 追加後に load イベントを付けても無意味
$(function(){
    var iframe = $('<iframe>').appendTo('body');
    iframe.load(function(){
        var ibody = this.contentWindow.document.body;
        $(ibody).append('<div>ブロック要素</div>');
    });
});
[環境]
  • Windows 7 64bit
  • Internet Explorer 11
  • Firefox 42.0
  • Chrome 48.0.2564.8

2015年10月31日

highlight.js で行番号を表示

ブログ内の SyntaxHighlighterhighlight.js 移行作業がようやく終わったので投稿。

highlight.js には行番号を表示する機能が付いてない。
とりあえず検索してみたところ、ちょうどいいプラグインがあったのでそれの紹介。(当ブログでも使用中)
wcoder/highlightjs-line-numbers.js

使用方法は、highlight.js を適用した要素(普通は <code>)に対して hljs.lineNumbersBlock(要素) を実行。
もしくは、hljs.initLineNumbersOnLoad() を仕掛けておく。
[使用例]

[].forEach.call(document.querySelectorAll('pre > code'), function(elem){
    // highlight.js は <code> 内の先頭と末尾の改行を無視してくれないので、ここで削除
    // ※ HTML を書く際、先頭と末尾に改行を入れない方法もある
    elem.textContent = elem.textContent.replace(/^[\r\n]+|[\r\n]+$/g, '');

    // highlight.js の適用
    hljs.highlightBlock(elem);

    // highlightjs-line-numbers.js の適用
    hljs.lineNumbersBlock(elem);
});

また、行番号ブロックのスタイル(.hljs-line-numbers)を自分で用意する必要がある。
[使用例(当ブログのスタイル・2015/10/31)]

pre > code.hljs.hljs-line-numbers {
    background-color: #f5f5f5;
    border-right: 0 none;
    color: #707070;
    text-align: right;
    min-width: 14px;
    /* 以降は選択できないようにするためのスタイル */
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

highlight.js に移行した理由

  • C# の新しいキーワードに対応 (素の SyntaxHighlighter は var 辺りから未対応)
  • 旧 Visual Studio 風ハイライトが選択可能
  • ハイライトする言語を選択してパッキングと Minify が公式サイトで可能
  • 外部ファイルの数が少ない (プラグインを除けば js と css の 2 ファイル)

2015年9月25日

python の加算代入演算子(+=)(プラスイコール)の注意点

python の一部のオブジェクト(list 等)では、「a = a + b」と「a += b」が同一視できない。
他言語経験者が思わぬバグを埋め込んでしまいそうな罠仕様・・・


>>> A = a = [1]
>>> a = a + [2]
>>> a, A, a is A
([1, 2], [1], False)
>>> # list の加算(+)は、元の a を変更しない
... # 加算の結果(新しい領域)を a に代入しているので、a と A は別オブジェクト
...

>>> B = b = [1]
>>> b += [2]
>>> b, B, b is B
([1, 2], [1, 2], True)
>>> # list の加算代入(+=)は、元の b を変更する
... # 加算代入しても新しい領域は割り当てられないので、b と B は同一オブジェクト
...

この理由は、list には 加算代入演算子(+=)用の __iadd__ メソッドが定義してあり、加算演算子(+)用の __add__ メソッド とは異なる動作になっているため。
※ list.__iadd__ の動作は list.extend と同じ。

__iadd__ メソッドが定義されてないオブジェクト(tuple 等)は、加算代入でも __add__ メソッドが使用される。
つまり、「a = a + b」と「a += b」 が同一視できる。

組み込みオブジェクトの大半には __iadd__ メソッドが定義されていないため、基本的に list とそのサブクラスを扱う際に注意すればいい。
加算代入演算子(+=)だけでなく、乗算代入演算子(*=)なども同様に注意。
ちなみに、__i???__ メソッドは、ミュータブルなオブジェクトに実装されるものらしい。

参考URL

2015年9月24日

python の代入は値を返さないけど多重代入は可能

python の代入は値を返さないので、下記のような書き方はエラーになる。

>>> # ファイルから少しずつ読み込んで処理
...
>>> f = open('hoge.txt', 'rb')
>>> while chunk = f.read(4096):
  File "", line 1
    while chunk = f.read(4096):
                ^
SyntaxError: invalid syntax

だけど、多重代入 (マルチターゲット代入 / chained assignment) はできる。

>>> a = b = 'test'
>>> a, b
('test', 'test')

何となく納得いかない感があるけど、多重代入だけは特別らしい。

下記のような書き方でも多重代入は可能。(自分がかつてやっていた方法)
左辺変更時に修正点多いし、あまりリーダブルじゃないと思うので使わない方がいい・・・

>>> a, b = ('test',) * 2
>>> a, b
('test', 'test')

おまけ

最初の例を綺麗に書きたい場合、iterfunctools.partial を組み合わせて使う。

>>> # ファイルから少しずつ読み込んで処理
...
>>> f = open('hoge.txt', 'rb')
>>> for chunk in iter(functools.partial(f.read, 4096), b''):
...    print(chunk)
...

>>> # partial の代わりにラムダ式でも可能
...
>>> for chunk in iter(lambda: f.read(4096), b''):
...    print(chunk)
...

2015年9月7日

python の等価演算子(==)と bool / 数値型

python の == 演算子は割と厳密で、公式ドキュメントにもあるとおり、数値型を除いて異なる型同士は等価にはならない。
javascript みたいに 1 == "1" が成立したりしない。
そのためか、python には === 演算子がない。

ただし、注意する点が 1 つあって、bool 型が int 型のサブクラスで、「True = 1」「False = 0」であること。
つまり、bool 型も数値型に含まれることになる。
そのため、下記のような結果になる。

>>> isinstance(False, int)
True
>>> isinstance(True, int)
True
>>> False == 0
True
>>> True == 1 == float(1) == complex(1)
True
>>> [True, False] == [1, 0]
True
>>> (True, False) == (1, 0)
True

上記を考慮して実装すれば基本的に問題ないのだが、どうしても厳密比較したい場合、対 bool 型であれば is 演算子を使う。

>>> value = True
>>> value is True
True
>>> value = 1
>>> value is True
False
対数値型であれば値の比較に加えて型の比較が必要になる。

>>> def isnum(value):
...   return type(value) in (int, float, complex)
...
>>> value = 1
>>> isnum(value) and value == 1
True
>>> value = True
>>> isnum(value) and value == 1
False

余談だが、python2 系では、True と False に値を代入して書き換えることができる。
割と危ない仕様・・・
python3 からはちゃんとエラーになってくれる。

2015年8月22日

document.querySelector で先頭が数字の ID を指定する方法

document.querySelectordocument.querySelectorAll で、数字から始まる ID をそのまま指定するとエラーになる。
document.getElementById はエラーにならず取得できる。
※ DOM じゃないけど、jquery セレクタもエラーにならず取得可能。

console.log(document.querySelector('#20150821abc'));
// Error: An invalid or illegal string was specified

console.log(document.getElementById('20150821abc'));
// <div id="20150821abc">

// 上記の div 要素は、このページ内に含まれているので、firebug・開発者ツールなどで確認可能

解決手段

querySelector は CSS セレクタを指定するものなので、CSS セレクタのエスケープ方法(コードポイント指定)で、先頭数字の ID を指定することができる。

// エスケープ方法は 2 通り
console.log(document.querySelector('#\\32 0150821abc'));    // '\3' + (digit) + ' '
console.log(document.querySelector('#\\0000320150821abc')); // '\00003' + (digit)

// <div id="20150821abc">

// もちろん、CSS の記述でも使用可能

自分用の GM スクリプトを作った際、先頭数字の ID を使っている web サイトがあったので・・・
普通は、先頭数字にはしないと思うのでレアケース?

参考URL

2015年7月3日

非同期 ASP.NET MVC と HttpContext.Current

非同期な ASP.NET MVC で HttpContext.Current を使うのはやめた方がいい、という話。

[非同期メソッドのサンプル]

public class MyController : Controller
{
    // RouteConfig で URL が割り当てられた非同期メソッド
    public async Task<ActionResult> Test()
    {
        // (A)

        await Task.Run(() => {
            // (B)
        });

        // (C)

        return View();
    }
}

上記で HttpContext.Current が使用できるのは (A) (B) (C) のどこか?
と疑問になったので調査してみた。

[結果]
(A) 使用可能
(B) 使用不可
(C) web.config の設定で変わる
<appSettings><add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /> を設定、または、<system.web><httpRuntime targetFramework="4.5"> (4.5 以上なら OK) を設定すると使用可能
上記を設定してない場合は使用不可
※ Windows 7/IIS 7.5/ASP.NET MVC 5/.NET 4.5.2
※ Visual Studio 付属の IIS Express は異なる結果になる場合あり

ちなみに、真ん中の Task を ConfigureAwait(false) とすると、設定に関わらず (C) でも HttpContext.Current 使用不可。

まとめ

調査結果のとおりなのだが、非同期だと HttpContext.Current は設定やコードに依存して使えたり使えなかったりするので、使わない方がまし・・・使用禁止でもいいと思う。

そもそも ASP.NET MVC の Controller クラスには HttpContext プロパティがあるので、これを使うのが普通。
Controller から呼び出すロジッククラスでは、HttpContext を引数渡しで使うのが無難。コードが増えるけど・・・
当たり前だけど HttpContext クラスにできるだけ依存しないクラス設計が大事。

参考URL

2015年6月30日

XmlSerializer と CDATA セクション

XmlSerializerCDATA セクションを扱うのが面倒だったので、自分なりのまとめ。(ぐぐれば引っかかる内容。)
System.Xml.Serialization 名前空間には、なぜか CDATA セクションのためのカスタム属性が用意されてない・・・

CDATA セクションのみを含む要素

[XML]

<root>
  <text><![CDATA[<b>cdata</b>]]></text>
</root>
[対応クラス]

[XmlRoot("root")]
public class CDataXml1
{
    [XmlElement("text")]
    public XmlCDataSection Text { get; set; }
}

XmlCDataSection のプロパティを用意するだけ。
XmlCDataSection は new XmlDocument().CreateCDataSection("CDATA セクションの中身") で作成できる。
面倒な場合はプロパティで工夫。
※シリアライズ時、CreateCDataSection の引数が null でも <![CDATA[]]> は出力される。

XmlCDataSection の代わりに、IXmlSerializable を継承した CDATA セクション用クラスを自作するのもあり。
[CDATA セクション用クラス]

public class CDataSection : IXmlSerializable
{
    public CDataSection() { }
    public CDataSection(string text) { Text = text; }
    public string Text { get; set; }

    public virtual XmlSchema GetSchema()
    {
        return null;
    }
    public virtual void ReadXml(XmlReader reader)
    {
        Text = reader.ReadElementString();
    }
    public virtual void WriteXml(XmlWriter writer)
    {
        writer.WriteCData(Text);
    }
}

CDATA セクションと属性がある要素

[XML]

<root>
  <node attr="value"><![CDATA[<b>cdata</b>]]></node>
</root>
[対応クラス]

[XmlRoot("root")]
public class CDataXml3
{
    [XmlElement("node")]
    public CDataNode Node { get; set; }
}

public class CDataNode
{
    [XmlAttribute("attr")]
    public string Attr { get; set; }

    // データの設定・取得用
    [XmlIgnore]
    public string Content { get; set; }

    // シリアライズ・デシリアライズ用
    [XmlText]
    public XmlNode[] ContentNode
    {
        get { return new[] { new XmlDocument().CreateCDataSection(Content) }; }
        set
        {
            if( value == null ){
                Content = null;
                return;
            }
            if( value.Length != 1 ) throw new InvalidOperationException();
            Content = value[0].Value;
        }
    }
}

CDATA セクションにあたるプロパティを、XmlCDataSection を 1 つだけ含む XmlNode[] 型にして、XmlTextAttribute を付加。
そうすると、直接 CDATA セクションを出力できる。
XmlNode[] 型だとデータの設定・取得が面倒なので、別のプロパティ(上記だと Content)を用意する。

ただし、この方法はデシリアライズ時にちょっとした問題があり、CDATA セクションの代わりに普通のテキストノードでもデシリアライズできてしまう。
デシリアライズされたインスタンス(上記だと value[0])は、元が CDATA セクションかテキストノードかに関わらず XmlText になるため、型チェックではじくことができない。
OuterXmlInnerText などのプロパティにも <![CDATA[]]> の文字列が含まれないため、デシリアライズインスタンスからはおそらく判断不可能。

CDATA セクションかテキストノードかどちらでもいい場合は問題ないが、厳密にチェックしたい場合、この方法では難しいかもしれない。

参考URL

2015年6月20日

クラスと構造体のインターフェースメソッド呼び出し

IComparer<T>IEqualityComparer<T> などのインターフェースを実装する際、クラスと構造体のどちらがいいのか迷ったことがあったので、インターフェースにキャストしたクラス・構造体からの、メソッド呼び出し速度を測定してみた。

[測定コード]

static class MeasureMethodCall
{
    static void Main(string[] args)
    {
        var size = 10000000;
        var cmpc = new ComparerClass();  // クラスベース
        var cmps = new ComparerStruct(); // 構造体ベース

        for( var i=0; i<11; i++ ){
            Console.WriteLine("[{0}]", i);

            // インターフェースにキャストした状態からのメソッド呼び出し
            InterfaceCall(size, cmpc);
            InterfaceCall(size, cmps);

            // おまけ:直接のメソッド呼び出し
            DirectCall(size, cmpc);
            DirectCall(size, cmps);
        }
    }

    static void InterfaceCall(int size, IComparer<int> comparer)
    {
        var sw = Stopwatch.StartNew();
        for( var i=0; i<size; i++ ) comparer.Compare(0, i);
        sw.Stop();
        Console.WriteLine("[InterfaceCall][{0}][{1:struct;0;class }] {2}",
            size, comparer.GetType().IsValueType.GetHashCode(), sw.Elapsed);
    }

    static void DirectCall<T>(int size, T comparer) where T : IComparer<int>
    {
        var sw = Stopwatch.StartNew();
        for( var i=0; i<size; i++ ) comparer.Compare(0, i);
        sw.Stop();
        Console.WriteLine("[DirectCall   ][{0}][{1:struct;0;class }] {2}",
            size, comparer.GetType().IsValueType.GetHashCode(), sw.Elapsed);
    }
}

class ComparerClass : IComparer<int>
{
    public int Compare(int x, int y){ return x.CompareTo(y); }
}

struct ComparerStruct : IComparer<int>
{
    public int Compare(int x, int y){ return x.CompareTo(y); }
}
[結果]
インターフェース(クラス) 0.0758722
インターフェース(構造体) 0.1012821
クラス 0.0674410
構造体 0.0083828
※コマンドプロンプトで実施(csc /optimize)
※2 回目以降は類似の結果だったため、2 回目の結果を採択
※単位は[s]

まとめると、
構造体 ≫ クラス > インターフェース(クラス) > インターフェース(構造体)
※左の方が速い

当たり前だが、インターフェースにキャストした状態からのメソッド呼び出しは、直接呼び出しよりも遅い。
そして、インターフェースにキャストした状態では、クラスより構造体の方が遅い。
理由はたぶん、インターフェース(構造体)のメソッド呼び出しは、自身(構造体インスタンス)のボックス化解除が必要だから?

インターフェースを引数に取るメソッドが、上記のように where 制約で実装されていれば構造体がベストだが、.NET Framework の標準メソッドの多くはインタフェース引数に where 制約を使っていない。
where 制約を使うと、型引数が増えて呼び出し側での指定が必要になり、コードが汚くなるからだと思われる。(参考:メソッドの型推論で型パラメータの制約は使われない

結論としては、一般的な使い方をするなら、インターフェースはクラスで実装した方がいい。
※プロパティも実質メソッドなので、プロパティを実装するインターフェースについても同様

実際に Array.SortEnumerable.OrderBy で測定したところ、インターフェース(クラス)の方がいい結果になった。

検証環境

Windows 7 64bit/.NET 4.5.2
Intel(R) Celeron(R) CPU G530 @ 2.40GHz/DDR3 8GB(4GB x 2)

2015年6月5日

LINQ OrderByDescending != OrderBy + Reverse

LINQ でシーケンスを降順にソートする場合、素直に OrderByDescending を使う方法と、OrderByReverse を組み合わせる方法がある。
(降順ソート用 IComparer<T> を使う方法もあるが、OrderByDescending と同じなので割愛)

この 2 つの方法は、異なる結果になることがある。
理由は、OrderByDescending と OrderBy が両方とも 安定ソートであるため。
「降順の安定ソートってどっち?」と混乱するかもしれないが、OrderByDescending の実装は MSDN の記載通り同じキーを持つ要素の順序は保持される。
OrderBy も同様なので、同じキーを持つ要素については、OrderByDescending と OrderBy は同じ順序になってしまう。

つまり、両者は正反対の順序にならないため、OrderBy + Reverse は必ずしも OrderByDescending の結果と一致しない。
※並び替えキーに重複がない場合は一致する。
※要素=並び替えキーとなる場合は、順序が違っても分からないため、考慮不要

[不一致ケース]

// インデックス付き配列を並べ替え
var data = new[]{ 'a', 'b', 'a', 'c', 'a' }.Select((V, I) => new{ V, I });

Console.WriteLine("[OrderBy]");
Console.WriteLine(string.Join(" ", data.OrderBy(v => v.V)));

Console.WriteLine("[OrderBy + Reverse]");
Console.WriteLine(string.Join(" ", data.OrderBy(v => v.V).Reverse()));

Console.WriteLine("[OrderByDescending]");
Console.WriteLine(string.Join(" ", data.OrderByDescending(v => v.V)));
[結果]
[OrderBy]
{ V = a, I = 0 } { V = a, I = 2 } { V = a, I = 4 } { V = b, I = 1 } { V = c, I = 3 }
[OrderBy + Reverse]
{ V = c, I = 3 } { V = b, I = 1 } { V = a, I = 4 } { V = a, I = 2 } { V = a, I = 0 }
[OrderByDescending]
{ V = c, I = 3 } { V = b, I = 1 } { V = a, I = 0 } { V = a, I = 2 } { V = a, I = 4 }

2015年5月23日

ASP.NET 出力レスポンスをキャプチャする方法

ブラウザに出力される HTML や API の出力などのレスポンスを、サーバー側で取得したい場合がたまにある。
ASP.NET (not MVC) でこれを行う方法はいくつかある。

Response.Filter を使う方法

Response.Filter にフィルタクラス (Stream 継承) を設定すると、レスポンスを出力前に加工することができる。
加工目的でなくても、Stream.Write に出力内容が入ってくるので、その内容を MemoryStream 等に入れればレスポンスをキャプチャできる。

[キャプチャ用フィルタクラス実装例]

public class CaptureStream : Stream
{
    public CaptureStream(Stream targetStream)
    {
        _targetStream = targetStream;
        Captured = new MemoryStream();
    }
    private readonly Stream _targetStream;
    public MemoryStream Captured { get; private set; }

    public override void Write(byte[] buffer, int offset, int count)
    {
        // 対象のストリームとメモリに書込み
        _targetStream.Write(buffer, offset, count);
        Captured.Write(buffer, offset, count);
    }

    public override void Flush()
    {
        _targetStream.Flush();

        // ここで Captured に対する処理を行う (ログ出力など)
        // OnFlush イベントを用意するとベター
    }

    public override void Close()
    {
        _targetStream.Close();
        Captured.Close();
        base.Close();

        // Captured に対する処理は、ここでも可能
        // MemoryStream は Close してもバッファは残るので ToArray などが可能
    }

    // 残りのオーバーライドは適宜実装
}

//---- 使用例 ----//
// Page クラス内の OnLoad などで
Response.Filter = new CaptureStream(Response.Filter);

デメリットは、キャプチャしたデータにアクセスできる Page イベント(オーバーライド可能メソッドも含む)が無いこと。
つまり、Page ライフサイクル内にキャプチャしたデータを処理するタイミングはなく、上記コードのようにフィルタクラスの Flush ないし Close 時に処理を挟むことしかできない。
そして、Page ライフサイクルから外れるため、セッションに保存できない。

余談だが、Response.Filter は ASP.NET MVC にも存在しているので、同じことができる。

Page.Render を使う方法

Page.Render をオーバーライドすると、出力されるレスポンスデータにアクセスできる。
→ そのままキャプチャ可能。

[Page.Render のキャプチャ実装例]

protected override void Render(HtmlTextWriter writer)
{
    string captured = null;

    using( var sw = new StringWriter() )
    using( var htw = new HtmlTextWriter(sw) ){
        // メモリ内にレスポンス出力
        base.Render(htw);
        captured = sw.ToString();

        // 文字列化したレスポンスをブラウザに出力
        writer.Write(captured);
    }

    // captured に対する処理を行う
}

この方法は、Page ライフサイクル内の処理なので、セッションに保存できる。

デメリットは、オーバーライドなので、フィルタクラスのように機能分離できないこと。

参考URL